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