Device state
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / LifecycleConductorImpl.java
1 /*
2  * Copyright (c) 2016 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
9 package org.opendaylight.openflowplugin.impl;
10
11 import com.google.common.annotations.VisibleForTesting;
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.ListenableFuture;
16 import io.netty.util.HashedWheelTimer;
17 import io.netty.util.Timeout;
18 import io.netty.util.TimerTask;
19 import java.util.Map;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.atomic.AtomicLong;
23 import javax.annotation.Nonnull;
24 import javax.annotation.Nullable;
25
26 import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
27 import org.opendaylight.openflowplugin.api.openflow.OFPManager;
28 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
29 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
30 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
31 import org.opendaylight.openflowplugin.api.openflow.device.DeviceManager;
32 import org.opendaylight.openflowplugin.api.openflow.lifecycle.DeviceContextChangeListener;
33 import org.opendaylight.openflowplugin.api.openflow.lifecycle.LifecycleConductor;
34 import org.opendaylight.openflowplugin.api.openflow.lifecycle.RoleChangeListener;
35 import org.opendaylight.openflowplugin.api.openflow.lifecycle.ServiceChangeListener;
36 import org.opendaylight.openflowplugin.api.openflow.rpc.RpcContext;
37 import org.opendaylight.openflowplugin.api.openflow.rpc.RpcManager;
38 import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsContext;
39 import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsManager;
40 import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageIntelligenceAgency;
41 import org.opendaylight.openflowplugin.extension.api.ExtensionConverterProviderKeeper;
42 import org.opendaylight.openflowplugin.extension.api.core.extension.ExtensionConverterProvider;
43 import org.opendaylight.openflowplugin.impl.util.MdSalRegistrationUtils;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 /**
49  */
50 final class LifecycleConductorImpl implements LifecycleConductor, RoleChangeListener, DeviceContextChangeListener, ExtensionConverterProviderKeeper {
51
52     private static final Logger LOG = LoggerFactory.getLogger(LifecycleConductorImpl.class);
53     private static final int TICKS_PER_WHEEL = 500;
54     private static final long TICK_DURATION = 10; // 0.5 sec.
55
56     private final HashedWheelTimer hashedWheelTimer = new HashedWheelTimer(TICK_DURATION, TimeUnit.MILLISECONDS, TICKS_PER_WHEEL);
57     private ExtensionConverterProvider extensionConverterProvider;
58     private DeviceManager deviceManager;
59     private StatisticsManager statisticsManager;
60     private RpcManager rpcManager;
61     private final MessageIntelligenceAgency messageIntelligenceAgency;
62     private ConcurrentHashMap<DeviceInfo, ServiceChangeListener> serviceChangeListeners = new ConcurrentHashMap<>();
63     private NotificationPublishService notificationPublishService;
64
65     LifecycleConductorImpl(final MessageIntelligenceAgency messageIntelligenceAgency) {
66         this.messageIntelligenceAgency = Preconditions.checkNotNull(messageIntelligenceAgency);
67     }
68
69     @Override
70     public ExtensionConverterProvider getExtensionConverterProvider() {
71         return extensionConverterProvider;
72     }
73
74     @Override
75     public void setExtensionConverterProvider(ExtensionConverterProvider extensionConverterProvider) {
76         this.extensionConverterProvider = extensionConverterProvider;
77     }
78
79     @Override
80     public void setSafelyManager(final OFPManager manager){
81         if (manager instanceof RpcManager) {
82             if (rpcManager != null) {
83                 LOG.info("RPC manager {} is already defined in conductor. ", manager);
84                 return;
85             }
86             this.rpcManager = (RpcManager) manager;
87         } else {
88             if (manager instanceof StatisticsManager) {
89                 if (statisticsManager != null) {
90                     LOG.info("Statistics manager {} is already defined in conductor. ", manager);
91                     return;
92                 }
93                 this.statisticsManager = (StatisticsManager) manager;
94             } else {
95                 if (manager instanceof DeviceManager) {
96                     if (deviceManager != null) {
97                         LOG.info("Device manager {} is already defined in conductor. ", manager);
98                         return;
99                     }
100                     this.deviceManager = (DeviceManager) manager;
101                 }
102             }
103         }
104     }
105
106     @Override
107     public void addOneTimeListenerWhenServicesChangesDone(final ServiceChangeListener manager, final DeviceInfo deviceInfo){
108         LOG.debug("Listener {} for service change for node {} registered.", manager, deviceInfo.getNodeId());
109         serviceChangeListeners.put(deviceInfo, manager);
110     }
111
112     @VisibleForTesting
113     void notifyServiceChangeListeners(final DeviceInfo deviceInfo, final boolean success){
114         if (serviceChangeListeners.size() == 0) {
115             return;
116         }
117         LOG.debug("Notifying registered listeners for service change, no. of listeners {}", serviceChangeListeners.size());
118         for (final Map.Entry<DeviceInfo, ServiceChangeListener> nodeIdServiceChangeListenerEntry : serviceChangeListeners.entrySet()) {
119             if (nodeIdServiceChangeListenerEntry.getKey().equals(deviceInfo)) {
120                 LOG.debug("Listener {} for service change for node {} was notified. Success was set on {}", nodeIdServiceChangeListenerEntry.getValue(), deviceInfo, success);
121                 nodeIdServiceChangeListenerEntry.getValue().servicesChangeDone(deviceInfo, success);
122                 serviceChangeListeners.remove(deviceInfo);
123             }
124         }
125     }
126
127     @Override
128     public void roleInitializationDone(final DeviceInfo deviceInfo, final boolean success) {
129         if (!success) {
130             LOG.warn("Initialization phase for node {} in role context was NOT successful, closing connection.", deviceInfo);
131             closeConnection(deviceInfo);
132         } else {
133             LOG.info("initialization phase for node {} in role context was successful, continuing to next context.", deviceInfo);
134         }
135     }
136
137     public void closeConnection(final DeviceInfo deviceInfo) {
138         LOG.debug("Close connection called for node {}", deviceInfo);
139         final DeviceContext deviceContext = getDeviceContext(deviceInfo);
140         if (null != deviceContext) {
141             deviceManager.notifyDeviceValidListeners(deviceInfo, false);
142             deviceContext.shutdownConnection();
143         }
144     }
145
146     @Override
147     public void roleChangeOnDevice(final DeviceInfo deviceInfo, final boolean success, final OfpRole newRole, final boolean initializationPhase) {
148
149         final DeviceContext deviceContext = getDeviceContext(deviceInfo);
150
151         if (null == deviceContext) {
152             LOG.warn("Something went wrong, device context for nodeId: {} doesn't exists");
153             return;
154         }
155         if (!success) {
156             LOG.warn("Role change to {} in role context for node {} was NOT successful, closing connection", newRole, deviceInfo);
157             closeConnection(deviceInfo);
158         } else {
159             if (initializationPhase) {
160                 LOG.debug("Initialization phase skipping starting services.");
161                 return;
162             }
163
164             LOG.info("Role change to {} in role context for node {} was successful.", newRole, deviceInfo);
165
166             final String logText;
167
168             if (OfpRole.BECOMEMASTER.equals(newRole)) {
169                 logText = "Start";
170                 statisticsManager.startScheduling(deviceInfo);
171                 MdSalRegistrationUtils.registerMasterServices(
172                         rpcManager.gainContext(deviceInfo),
173                         deviceContext,
174                         OfpRole.BECOMEMASTER,
175                         this.extensionConverterProvider);
176                 if (((RpcContext)rpcManager.gainContext(deviceInfo)).isStatisticsRpcEnabled()) {
177                     MdSalRegistrationUtils.registerStatCompatibilityServices(
178                             rpcManager.gainContext(deviceInfo),
179                             deviceManager.gainContext(deviceInfo),
180                             notificationPublishService,
181                             new AtomicLong());
182                 }
183             } else {
184                 logText = "Stopp";
185                 statisticsManager.stopScheduling(deviceInfo);
186                 MdSalRegistrationUtils.registerSlaveServices(
187                         rpcManager.gainContext(deviceInfo),
188                         OfpRole.BECOMESLAVE);
189             }
190
191             final ListenableFuture<Void> onClusterRoleChange = deviceManager.onClusterRoleChange(deviceInfo, newRole);
192             Futures.addCallback(onClusterRoleChange, new FutureCallback<Void>() {
193                 @Override
194                 public void onSuccess(@Nullable final Void aVoid) {
195                     LOG.info("{}ing services for node {} was successful", logText, deviceInfo);
196                     if (newRole.equals(OfpRole.BECOMESLAVE)) notifyServiceChangeListeners(deviceInfo, true);
197                 }
198
199                 @Override
200                 public void onFailure(final Throwable throwable) {
201                     LOG.warn("ing services for node {} was NOT successful, closing connection", logText, deviceInfo);
202                     closeConnection(deviceInfo);
203                 }
204             });
205         }
206     }
207
208     public MessageIntelligenceAgency getMessageIntelligenceAgency() {
209         return messageIntelligenceAgency;
210     }
211
212     @Override
213     public DeviceContext getDeviceContext(DeviceInfo deviceInfo){
214          return deviceManager.gainContext(deviceInfo);
215     }
216
217     @Override
218     public StatisticsContext getStatisticsContext(DeviceInfo deviceInfo){
219         return statisticsManager.gainContext(deviceInfo);
220     }
221
222     public Timeout newTimeout(@Nonnull TimerTask task, long delay, @Nonnull TimeUnit unit) {
223         return hashedWheelTimer.newTimeout(task, delay, unit);
224     }
225
226     @Override
227     public ConnectionContext.CONNECTION_STATE gainConnectionStateSafely(final DeviceInfo deviceInfo){
228         return (null != getDeviceContext(deviceInfo)) ? getDeviceContext(deviceInfo).getPrimaryConnectionContext().getConnectionState() : null;
229     }
230
231     @Override
232     public Long reserveXidForDeviceMessage(final DeviceInfo deviceInfo){
233         return null != getDeviceContext(deviceInfo) ? getDeviceContext(deviceInfo).reserveXidForDeviceMessage() : null;
234     }
235
236     @Override
237     public void deviceStartInitializationDone(final DeviceInfo deviceInfo, final boolean success) {
238         if (!success) {
239             LOG.warn("Initialization phase for node {} in device context was NOT successful, closing connection.", deviceInfo);
240             closeConnection(deviceInfo);
241         } else {
242             LOG.info("initialization phase for node {} in device context was successful. Continuing to next context.", deviceInfo);
243         }
244     }
245
246     @Override
247     public void deviceInitializationDone(final DeviceInfo deviceInfo, final boolean success) {
248         if (!success) {
249             LOG.warn("Initialization phase for node {} in device context was NOT successful, closing connection.", deviceInfo);
250             closeConnection(deviceInfo);
251         } else {
252             LOG.info("initialization phase for node {} in device context was successful. All phases initialized OK.", deviceInfo);
253         }
254     }
255
256     @VisibleForTesting
257     boolean isServiceChangeListenersEmpty() {
258         return this.serviceChangeListeners.isEmpty();
259     }
260
261     @Override
262     public NotificationPublishService getNotificationPublishService() {
263         return notificationPublishService;
264     }
265
266     @Override
267     public void setNotificationPublishService(NotificationPublishService notificationPublishService) {
268         this.notificationPublishService = notificationPublishService;
269     }
270 }