Bug 5596 Created lifecycle service
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / statistics / StatisticsContextImpl.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
9 package org.opendaylight.openflowplugin.impl.statistics;
10
11 import com.google.common.annotations.VisibleForTesting;
12 import com.google.common.base.Preconditions;
13 import com.google.common.collect.ImmutableList;
14 import com.google.common.collect.Iterators;
15 import com.google.common.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.Futures;
17 import com.google.common.util.concurrent.ListenableFuture;
18 import com.google.common.util.concurrent.SettableFuture;
19 import io.netty.util.Timeout;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Optional;
26 import javax.annotation.Nonnull;
27 import javax.annotation.Nullable;
28 import javax.annotation.concurrent.GuardedBy;
29 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
30 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
31 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
32 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
33 import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
34 import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
35 import org.opendaylight.openflowplugin.api.openflow.lifecycle.LifecycleConductor;
36 import org.opendaylight.openflowplugin.api.openflow.rpc.listener.ItemLifecycleListener;
37 import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsContext;
38 import org.opendaylight.openflowplugin.impl.rpc.AbstractRequestContext;
39 import org.opendaylight.openflowplugin.impl.rpc.listener.ItemLifecycleListenerImpl;
40 import org.opendaylight.openflowplugin.impl.services.RequestContextUtil;
41 import org.opendaylight.openflowplugin.impl.statistics.services.dedicated.StatisticsGatheringOnTheFlyService;
42 import org.opendaylight.openflowplugin.impl.statistics.services.dedicated.StatisticsGatheringService;
43 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.ConvertorExecutor;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 class StatisticsContextImpl implements StatisticsContext {
49
50     private static final Logger LOG = LoggerFactory.getLogger(StatisticsContextImpl.class);
51     private static final String CONNECTION_CLOSED = "Connection closed.";
52
53     private final ItemLifecycleListener itemLifeCycleListener;
54     private final Collection<RequestContext<?>> requestContexts = new HashSet<>();
55     private final DeviceContext deviceContext;
56     private final DeviceState devState;
57     private final ListenableFuture<Boolean> emptyFuture;
58     private final boolean shuttingDownStatisticsPolling;
59     private final Object COLLECTION_STAT_TYPE_LOCK = new Object();
60     private final SinglePurposeMultipartReplyTranslator multipartReplyTranslator;
61     @GuardedBy("COLLECTION_STAT_TYPE_LOCK")
62     private List<MultipartType> collectingStatType;
63
64     private StatisticsGatheringService statisticsGatheringService;
65     private StatisticsGatheringOnTheFlyService statisticsGatheringOnTheFlyService;
66     private Timeout pollTimeout;
67     private final DeviceInfo deviceInfo;
68
69     private volatile boolean schedulingEnabled;
70     private volatile CONTEXT_STATE state;
71
72     StatisticsContextImpl(@Nonnull final DeviceInfo deviceInfo,
73                           @Nonnull final boolean shuttingDownStatisticsPolling,
74                           @Nonnull final LifecycleConductor lifecycleConductor,
75                           @Nonnull final ConvertorExecutor convertorExecutor) {
76         this.deviceContext = Preconditions.checkNotNull(lifecycleConductor.getDeviceContext(deviceInfo));
77         this.devState = Preconditions.checkNotNull(deviceContext.getDeviceState());
78         this.shuttingDownStatisticsPolling = shuttingDownStatisticsPolling;
79         multipartReplyTranslator = new SinglePurposeMultipartReplyTranslator(convertorExecutor);
80         emptyFuture = Futures.immediateFuture(false);
81         statisticsGatheringService = new StatisticsGatheringService(this, deviceContext);
82         statisticsGatheringOnTheFlyService = new StatisticsGatheringOnTheFlyService(this, deviceContext, convertorExecutor);
83         itemLifeCycleListener = new ItemLifecycleListenerImpl(deviceContext);
84         statListForCollectingInitialization();
85         setState(CONTEXT_STATE.INITIALIZATION);
86         this.deviceInfo = deviceInfo;
87     }
88
89     @Override
90     public void statListForCollectingInitialization() {
91         synchronized (COLLECTION_STAT_TYPE_LOCK) {
92             final List<MultipartType> statListForCollecting = new ArrayList<>();
93             if (devState.isTableStatisticsAvailable()) {
94                 statListForCollecting.add(MultipartType.OFPMPTABLE);
95             }
96             if (devState.isFlowStatisticsAvailable()) {
97                 statListForCollecting.add(MultipartType.OFPMPFLOW);
98             }
99             if (devState.isGroupAvailable()) {
100                 statListForCollecting.add(MultipartType.OFPMPGROUPDESC);
101                 statListForCollecting.add(MultipartType.OFPMPGROUP);
102             }
103             if (devState.isMetersAvailable()) {
104                 statListForCollecting.add(MultipartType.OFPMPMETERCONFIG);
105                 statListForCollecting.add(MultipartType.OFPMPMETER);
106             }
107             if (devState.isPortStatisticsAvailable()) {
108                 statListForCollecting.add(MultipartType.OFPMPPORTSTATS);
109             }
110             if (devState.isQueueStatisticsAvailable()) {
111                 statListForCollecting.add(MultipartType.OFPMPQUEUE);
112             }
113             collectingStatType = ImmutableList.<MultipartType>copyOf(statListForCollecting);
114         }
115     }
116
117
118     @Override
119     public ListenableFuture<Boolean> initialGatherDynamicData() {
120         return gatherDynamicData(true);
121     }
122
123     @Override
124     public ListenableFuture<Boolean> gatherDynamicData(){
125         return gatherDynamicData(false);
126     }
127
128     private ListenableFuture<Boolean> gatherDynamicData(final boolean initial) {
129         if (shuttingDownStatisticsPolling) {
130             LOG.debug("Statistics for device {} is not enabled.", getDeviceInfo().getNodeId());
131             return Futures.immediateFuture(Boolean.TRUE);
132         }
133         final ListenableFuture<Boolean> errorResultFuture = deviceConnectionCheck();
134         if (errorResultFuture != null) {
135             return errorResultFuture;
136         }
137         synchronized (COLLECTION_STAT_TYPE_LOCK) {
138             final Iterator<MultipartType> statIterator = collectingStatType.iterator();
139             final SettableFuture<Boolean> settableStatResultFuture = SettableFuture.create();
140
141             // write start timestamp to state snapshot container
142             StatisticsGatheringUtils.markDeviceStateSnapshotStart(deviceContext);
143
144             statChainFuture(statIterator, settableStatResultFuture, initial);
145
146             // write end timestamp to state snapshot container
147             Futures.addCallback(settableStatResultFuture, new FutureCallback<Boolean>() {
148                 @Override
149                 public void onSuccess(@Nullable final Boolean result) {
150                     StatisticsGatheringUtils.markDeviceStateSnapshotEnd(deviceContext, true);
151                 }
152                 @Override
153                 public void onFailure(final Throwable t) {
154                     StatisticsGatheringUtils.markDeviceStateSnapshotEnd(deviceContext, false);
155                 }
156             });
157             return settableStatResultFuture;
158         }
159     }
160
161     private ListenableFuture<Boolean> chooseStat(final MultipartType multipartType, final boolean initial){
162         switch (multipartType) {
163             case OFPMPFLOW:
164                 return collectFlowStatistics(multipartType, initial);
165             case OFPMPTABLE:
166                 return collectTableStatistics(multipartType);
167             case OFPMPPORTSTATS:
168                 return collectPortStatistics(multipartType);
169             case OFPMPQUEUE:
170                 return collectQueueStatistics(multipartType);
171             case OFPMPGROUPDESC:
172                 return collectGroupDescStatistics(multipartType);
173             case OFPMPGROUP:
174                 return collectGroupStatistics(multipartType);
175             case OFPMPMETERCONFIG:
176                 return collectMeterConfigStatistics(multipartType);
177             case OFPMPMETER:
178                 return collectMeterStatistics(multipartType);
179             default:
180                 LOG.warn("Unsuported Statistics type {}", multipartType);
181                 return Futures.immediateCheckedFuture(Boolean.TRUE);
182         }
183     }
184
185
186     @Override
187     public <T> RequestContext<T> createRequestContext() {
188         final AbstractRequestContext<T> ret = new AbstractRequestContext<T>(deviceContext.reserveXidForDeviceMessage()) {
189             @Override
190             public void close() {
191                 requestContexts.remove(this);
192             }
193         };
194         requestContexts.add(ret);
195         return ret;
196     }
197
198     @Override
199     public void close() {
200         if (CONTEXT_STATE.TERMINATION.equals(getState())) {
201             if (LOG.isDebugEnabled()) {
202                 LOG.debug("Statistics context is already in state TERMINATION.");
203             }
204         } else {
205             setState(CONTEXT_STATE.TERMINATION);
206             schedulingEnabled = false;
207             for (final Iterator<RequestContext<?>> iterator = Iterators.consumingIterator(requestContexts.iterator());
208                  iterator.hasNext(); ) {
209                 RequestContextUtil.closeRequestContextWithRpcError(iterator.next(), CONNECTION_CLOSED);
210             }
211             if (null != pollTimeout && !pollTimeout.isExpired()) {
212                 pollTimeout.cancel();
213             }
214         }
215     }
216
217     @Override
218     public void setSchedulingEnabled(final boolean schedulingEnabled) {
219         this.schedulingEnabled = schedulingEnabled;
220     }
221
222     @Override
223     public boolean isSchedulingEnabled() {
224         return schedulingEnabled;
225     }
226
227     @Override
228     public void setPollTimeout(final Timeout pollTimeout) {
229         this.pollTimeout = pollTimeout;
230     }
231
232     @Override
233     public Optional<Timeout> getPollTimeout() {
234         return Optional.ofNullable(pollTimeout);
235     }
236
237     private void statChainFuture(final Iterator<MultipartType> iterator, final SettableFuture<Boolean> resultFuture, final boolean initial) {
238         if (ConnectionContext.CONNECTION_STATE.RIP.equals(deviceContext.getPrimaryConnectionContext().getConnectionState())) {
239             final String errMsg = String.format("Device connection is closed for Node : %s.",
240                     getDeviceInfo().getNodeId());
241             LOG.debug(errMsg);
242             resultFuture.setException(new IllegalStateException(errMsg));
243             return;
244         }
245         if ( ! iterator.hasNext()) {
246             resultFuture.set(Boolean.TRUE);
247             LOG.debug("Stats collection successfully finished for node {}", getDeviceInfo().getNodeId());
248             return;
249         }
250
251         final MultipartType nextType = iterator.next();
252         LOG.debug("Stats iterating to next type for node {} of type {}", getDeviceInfo().getNodeId(), nextType);
253
254         final ListenableFuture<Boolean> deviceStatisticsCollectionFuture = chooseStat(nextType, initial);
255         Futures.addCallback(deviceStatisticsCollectionFuture, new FutureCallback<Boolean>() {
256             @Override
257             public void onSuccess(final Boolean result) {
258                 statChainFuture(iterator, resultFuture, initial);
259             }
260             @Override
261             public void onFailure(@Nonnull final Throwable t) {
262                 resultFuture.setException(t);
263             }
264         });
265     }
266
267     /**
268      * Method checks a device state. It returns null for be able continue. Otherwise it returns immediateFuture
269      * which has to be returned from caller too
270      *
271      * @return
272      */
273     @VisibleForTesting
274     ListenableFuture<Boolean> deviceConnectionCheck() {
275         if (!ConnectionContext.CONNECTION_STATE.WORKING.equals(deviceContext.getPrimaryConnectionContext().getConnectionState())) {
276             ListenableFuture<Boolean> resultingFuture;
277             switch (deviceContext.getPrimaryConnectionContext().getConnectionState()) {
278                 case RIP:
279                     final String errMsg = String.format("Device connection doesn't exist anymore. Primary connection status : %s",
280                             deviceContext.getPrimaryConnectionContext().getConnectionState());
281                     resultingFuture = Futures.immediateFailedFuture(new Throwable(errMsg));
282                     break;
283                 default:
284                     resultingFuture = Futures.immediateCheckedFuture(Boolean.TRUE);
285                     break;
286             }
287             return resultingFuture;
288         }
289         return null;
290     }
291
292     //TODO: Refactor twice sending deviceContext into gatheringStatistics
293     private ListenableFuture<Boolean> collectFlowStatistics(final MultipartType multipartType, final boolean initial) {
294         return devState.isFlowStatisticsAvailable() ? StatisticsGatheringUtils.gatherStatistics(
295                 statisticsGatheringOnTheFlyService,
296                 getDeviceInfo(),
297                 /*MultipartType.OFPMPFLOW*/ multipartType,
298                 deviceContext,
299                 deviceContext,
300                 initial, multipartReplyTranslator) : emptyFuture;
301     }
302
303     private ListenableFuture<Boolean> collectTableStatistics(final MultipartType multipartType) {
304         return devState.isTableStatisticsAvailable() ? StatisticsGatheringUtils.gatherStatistics(
305                 statisticsGatheringService,
306                 getDeviceInfo(),
307                 /*MultipartType.OFPMPTABLE*/ multipartType,
308                 deviceContext,
309                 deviceContext,
310                 false, multipartReplyTranslator) : emptyFuture;
311     }
312
313     private ListenableFuture<Boolean> collectPortStatistics(final MultipartType multipartType) {
314         return devState.isPortStatisticsAvailable() ? StatisticsGatheringUtils.gatherStatistics(
315                 statisticsGatheringService,
316                 getDeviceInfo(),
317                 /*MultipartType.OFPMPPORTSTATS*/ multipartType,
318                 deviceContext,
319                 deviceContext,
320                 false, multipartReplyTranslator) : emptyFuture;
321     }
322
323     private ListenableFuture<Boolean> collectQueueStatistics(final MultipartType multipartType) {
324         return !devState.isQueueStatisticsAvailable() ? emptyFuture : StatisticsGatheringUtils.gatherStatistics(
325                 statisticsGatheringService,
326                 getDeviceInfo(),
327                 /*MultipartType.OFPMPQUEUE*/ multipartType,
328                 deviceContext,
329                 deviceContext,
330                 false, multipartReplyTranslator);
331     }
332
333     private ListenableFuture<Boolean> collectGroupDescStatistics(final MultipartType multipartType) {
334         return devState.isGroupAvailable() ? StatisticsGatheringUtils.gatherStatistics(
335                 statisticsGatheringService,
336                 getDeviceInfo(),
337                 /*MultipartType.OFPMPGROUPDESC*/ multipartType,
338                 deviceContext,
339                 deviceContext,
340                 false, multipartReplyTranslator) : emptyFuture;
341     }
342
343     private ListenableFuture<Boolean> collectGroupStatistics(final MultipartType multipartType) {
344         return devState.isGroupAvailable() ? StatisticsGatheringUtils.gatherStatistics(
345                 statisticsGatheringService,
346                 getDeviceInfo(),
347                 /*MultipartType.OFPMPGROUP*/ multipartType,
348                 deviceContext,
349                 deviceContext,
350                 false, multipartReplyTranslator) : emptyFuture;
351     }
352
353     private ListenableFuture<Boolean> collectMeterConfigStatistics(final MultipartType multipartType) {
354         return devState.isMetersAvailable() ? StatisticsGatheringUtils.gatherStatistics(
355                 statisticsGatheringService,
356                 getDeviceInfo(),
357                 /*MultipartType.OFPMPMETERCONFIG*/ multipartType,
358                 deviceContext,
359                 deviceContext,
360                 false, multipartReplyTranslator) : emptyFuture;
361     }
362
363     private ListenableFuture<Boolean> collectMeterStatistics(final MultipartType multipartType) {
364         return devState.isMetersAvailable() ? StatisticsGatheringUtils.gatherStatistics(
365                 statisticsGatheringService,
366                 getDeviceInfo(),
367                 /*MultipartType.OFPMPMETER*/ multipartType,
368                 deviceContext,
369                 deviceContext,
370                 false, multipartReplyTranslator) : emptyFuture;
371     }
372
373     @VisibleForTesting
374     void setStatisticsGatheringService(final StatisticsGatheringService statisticsGatheringService) {
375         this.statisticsGatheringService = statisticsGatheringService;
376     }
377
378     @VisibleForTesting
379     void setStatisticsGatheringOnTheFlyService(final StatisticsGatheringOnTheFlyService
380                                                              statisticsGatheringOnTheFlyService) {
381         this.statisticsGatheringOnTheFlyService = statisticsGatheringOnTheFlyService;
382     }
383
384     @Override
385     public ItemLifecycleListener getItemLifeCycleListener () {
386         return itemLifeCycleListener;
387     }
388
389     @Override
390     public CONTEXT_STATE getState() {
391         return this.state;
392     }
393
394     @Override
395     public void setState(CONTEXT_STATE state) {
396         this.state = state;
397     }
398
399     @Override
400     public ServiceGroupIdentifier getServiceIdentifier() {
401         return this.deviceInfo.getServiceIdentifier();
402     }
403
404     @Override
405     public DeviceInfo getDeviceInfo() {
406         return this.deviceInfo;
407     }
408 }