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