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