Disconnect node statistics tracking
[controller.git] / opendaylight / md-sal / statistics-manager / src / main / java / org / opendaylight / controller / md / statistics / manager / NodeStatisticsHandler.java
1 /*
2  * Copyright IBM Corporation, 2013.  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 package org.opendaylight.controller.md.statistics.manager;
9
10 import java.util.Collection;
11 import java.util.List;
12 import java.util.Timer;
13 import java.util.TimerTask;
14 import java.util.concurrent.TimeUnit;
15
16 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
17 import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableBuilder;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.AggregateFlowStatisticsData;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.AggregateFlowStatisticsDataBuilder;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsService;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.aggregate.flow.statistics.AggregateFlowStatisticsBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapList;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.OpendaylightFlowTableStatisticsService;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.and.statistics.map.FlowTableAndStatisticsMap;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev131103.TransactionAware;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev131103.TransactionId;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupFeatures;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupFeaturesBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.OpendaylightGroupStatisticsService;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.features.GroupFeaturesBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupFeatures;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.desc.stats.reply.GroupDescStats;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.statistics.reply.GroupStats;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterFeatures;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterFeaturesBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.OpendaylightMeterStatisticsService;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.MeterFeaturesBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterFeatures;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.config.stats.reply.MeterConfigStats;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.reply.MeterStats;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.AggregateFlowStatistics;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsService;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMap;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.OpendaylightQueueStatisticsService;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.queue.id.and.statistics.map.QueueIdAndStatisticsMap;
55 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 import com.google.common.base.Preconditions;
60
61 /**
62  * This class handles the lifecycle of per-node statistics. It receives data
63  * from StatisticsListener, stores it in the data store and keeps track of
64  * when the data should be removed.
65  *
66  * @author avishnoi@in.ibm.com
67  */
68 public final class NodeStatisticsHandler implements AutoCloseable, FlowCapableContext {
69     private static final Logger logger = LoggerFactory.getLogger(NodeStatisticsHandler.class);
70
71     private static final long STATS_COLLECTION_MILLIS = TimeUnit.SECONDS.toMillis(15);
72     private static final long FIRST_COLLECTION_MILLIS = TimeUnit.SECONDS.toMillis(5);
73     private static final int NUMBER_OF_WAIT_CYCLES = 2;
74
75     private final MultipartMessageManager msgManager;
76     private final InstanceIdentifier<Node> targetNodeIdentifier;
77     private final FlowStatsTracker flowStats;
78     private final FlowTableStatsTracker flowTableStats;
79     private final GroupDescStatsTracker groupDescStats;
80     private final GroupStatsTracker groupStats;
81     private final MeterConfigStatsTracker meterConfigStats;
82     private final MeterStatsTracker meterStats;
83     private final NodeConnectorStatsTracker nodeConnectorStats;
84     private final QueueStatsTracker queueStats;
85     private final DataProviderService dps;
86     private final NodeRef targetNodeRef;
87     private final NodeKey targetNodeKey;
88     private final TimerTask task = new TimerTask() {
89         @Override
90         public void run() {
91             requestPeriodicStatistics();
92             cleanStaleStatistics();
93         }
94     };
95
96     public NodeStatisticsHandler(final DataProviderService dps, final NodeKey nodeKey,
97             final OpendaylightFlowStatisticsService flowStatsService,
98             final OpendaylightFlowTableStatisticsService flowTableStatsService,
99             final OpendaylightGroupStatisticsService groupStatsService,
100             final OpendaylightMeterStatisticsService meterStatsService,
101             final OpendaylightPortStatisticsService portStatsService,
102             final OpendaylightQueueStatisticsService queueStatsService) {
103         this.dps = Preconditions.checkNotNull(dps);
104         this.targetNodeKey = Preconditions.checkNotNull(nodeKey);
105         this.targetNodeIdentifier = InstanceIdentifier.builder(Nodes.class).child(Node.class, targetNodeKey).build();
106         this.targetNodeRef = new NodeRef(targetNodeIdentifier);
107
108         final long lifetimeNanos = TimeUnit.MILLISECONDS.toNanos(STATS_COLLECTION_MILLIS * NUMBER_OF_WAIT_CYCLES);
109
110         msgManager = new MultipartMessageManager(lifetimeNanos);
111         flowStats = new FlowStatsTracker(flowStatsService, this, lifetimeNanos);
112         flowTableStats = new FlowTableStatsTracker(flowTableStatsService, this, lifetimeNanos);
113         groupDescStats = new GroupDescStatsTracker(groupStatsService, this, lifetimeNanos);
114         groupStats = new GroupStatsTracker(groupStatsService, this, lifetimeNanos);
115         meterConfigStats = new MeterConfigStatsTracker(meterStatsService, this, lifetimeNanos);
116         meterStats = new MeterStatsTracker(meterStatsService, this, lifetimeNanos);
117         nodeConnectorStats = new NodeConnectorStatsTracker(portStatsService, this, lifetimeNanos);
118         queueStats = new QueueStatsTracker(queueStatsService, this, lifetimeNanos);
119     }
120
121     public NodeKey getTargetNodeKey() {
122         return targetNodeKey;
123     }
124
125     @Override
126     public InstanceIdentifier<Node> getNodeIdentifier() {
127         return targetNodeIdentifier;
128     }
129
130     @Override
131     public NodeRef getNodeRef() {
132         return targetNodeRef;
133     }
134
135     @Override
136     public DataModificationTransaction startDataModification() {
137         return dps.beginTransaction();
138     }
139
140     public synchronized void updateGroupDescStats(TransactionAware transaction, Boolean more, List<GroupDescStats> list) {
141         if (msgManager.isExpectedTransaction(transaction, more)) {
142             groupDescStats.updateStats(list);
143         }
144     }
145
146     public synchronized void updateGroupStats(TransactionAware transaction, Boolean more, List<GroupStats> list) {
147         if (msgManager.isExpectedTransaction(transaction, more)) {
148             groupStats.updateStats(list);
149         }
150     }
151
152     public synchronized void updateMeterConfigStats(TransactionAware transaction, Boolean more, List<MeterConfigStats> list) {
153         if (msgManager.isExpectedTransaction(transaction, more)) {
154             meterConfigStats.updateStats(list);
155         }
156     }
157
158     public synchronized void updateMeterStats(TransactionAware transaction, Boolean more, List<MeterStats> list) {
159         if (msgManager.isExpectedTransaction(transaction, more)) {
160             meterStats.updateStats(list);
161         }
162     }
163
164     public synchronized void updateQueueStats(TransactionAware transaction, Boolean more, List<QueueIdAndStatisticsMap> list) {
165         if (msgManager.isExpectedTransaction(transaction, more)) {
166             queueStats.updateStats(list);
167         }
168     }
169
170     public synchronized void updateFlowTableStats(TransactionAware transaction, Boolean more, List<FlowTableAndStatisticsMap> list) {
171         if (msgManager.isExpectedTransaction(transaction, more)) {
172             flowTableStats.updateStats(list);
173         }
174     }
175
176     public synchronized void updateNodeConnectorStats(TransactionAware transaction, Boolean more, List<NodeConnectorStatisticsAndPortNumberMap> list) {
177         if (msgManager.isExpectedTransaction(transaction, more)) {
178             nodeConnectorStats.updateStats(list);
179         }
180     }
181
182     public synchronized void updateAggregateFlowStats(TransactionAware transaction, Boolean more, AggregateFlowStatistics flowStats) {
183         final Short tableId = msgManager.isExpectedTableTransaction(transaction, more);
184         if (tableId != null) {
185             final DataModificationTransaction trans = dps.beginTransaction();
186             InstanceIdentifier<Table> tableRef = InstanceIdentifier.builder(Nodes.class).child(Node.class, targetNodeKey)
187                     .augmentation(FlowCapableNode.class).child(Table.class, new TableKey(tableId)).toInstance();
188
189             AggregateFlowStatisticsDataBuilder aggregateFlowStatisticsDataBuilder = new AggregateFlowStatisticsDataBuilder();
190             AggregateFlowStatisticsBuilder aggregateFlowStatisticsBuilder = new AggregateFlowStatisticsBuilder(flowStats);
191
192             aggregateFlowStatisticsDataBuilder.setAggregateFlowStatistics(aggregateFlowStatisticsBuilder.build());
193
194             logger.debug("Augment aggregate statistics: {} for table {} on Node {}",
195                     aggregateFlowStatisticsBuilder.build().toString(),tableId,targetNodeKey);
196
197             TableBuilder tableBuilder = new TableBuilder();
198             tableBuilder.setKey(new TableKey(tableId));
199             tableBuilder.addAugmentation(AggregateFlowStatisticsData.class, aggregateFlowStatisticsDataBuilder.build());
200             trans.putOperationalData(tableRef, tableBuilder.build());
201
202             trans.commit();
203         }
204     }
205
206     public synchronized void updateFlowStats(TransactionAware transaction, Boolean more, List<FlowAndStatisticsMapList> list) {
207         if (msgManager.isExpectedTransaction(transaction, more)) {
208             flowStats.updateStats(list);
209         }
210     }
211
212     public synchronized void updateGroupFeatures(GroupFeatures notification) {
213         final DataModificationTransaction trans = dps.beginTransaction();
214
215         final NodeBuilder nodeData = new NodeBuilder();
216         nodeData.setKey(targetNodeKey);
217
218         NodeGroupFeaturesBuilder nodeGroupFeatures = new NodeGroupFeaturesBuilder();
219         GroupFeaturesBuilder groupFeatures = new GroupFeaturesBuilder(notification);
220         nodeGroupFeatures.setGroupFeatures(groupFeatures.build());
221
222         //Update augmented data
223         nodeData.addAugmentation(NodeGroupFeatures.class, nodeGroupFeatures.build());
224         trans.putOperationalData(targetNodeIdentifier, nodeData.build());
225
226         // FIXME: should we be tracking this data?
227         trans.commit();
228     }
229
230     public synchronized void updateMeterFeatures(MeterFeatures features) {
231         final DataModificationTransaction trans = dps.beginTransaction();
232
233         final NodeBuilder nodeData = new NodeBuilder();
234         nodeData.setKey(targetNodeKey);
235
236         NodeMeterFeaturesBuilder nodeMeterFeatures = new NodeMeterFeaturesBuilder();
237         MeterFeaturesBuilder meterFeature = new MeterFeaturesBuilder(features);
238         nodeMeterFeatures.setMeterFeatures(meterFeature.build());
239
240         //Update augmented data
241         nodeData.addAugmentation(NodeMeterFeatures.class, nodeMeterFeatures.build());
242         trans.putOperationalData(targetNodeIdentifier, nodeData.build());
243
244         // FIXME: should we be tracking this data?
245         trans.commit();
246     }
247
248     public synchronized void cleanStaleStatistics() {
249         final DataModificationTransaction trans = dps.beginTransaction();
250         final long now = System.nanoTime();
251
252         flowStats.cleanup(trans, now);
253         groupDescStats.cleanup(trans, now);
254         groupStats.cleanup(trans, now);
255         meterConfigStats.cleanup(trans, now);
256         meterStats.cleanup(trans, now);
257         nodeConnectorStats.cleanup(trans, now);
258         queueStats.cleanup(trans, now);
259         msgManager.cleanStaleTransactionIds();
260
261         trans.commit();
262     }
263
264     public synchronized void requestPeriodicStatistics() {
265         logger.debug("Send requests for statistics collection to node : {}", targetNodeKey);
266
267         flowTableStats.request();
268
269         // FIXME: it does not make sense to trigger this before sendAllFlowTablesStatisticsRequest()
270         //        comes back -- we do not have any tables anyway.
271         final Collection<TableKey> tables = flowTableStats.getTables();
272         logger.debug("Node {} supports {} table(s)", targetNodeKey, tables.size());
273         for (final TableKey key : tables) {
274             logger.debug("Send aggregate stats request for flow table {} to node {}", key.getId(), targetNodeKey);
275             flowStats.requestAggregateFlows(key);
276         }
277
278         flowStats.requestAllFlowsAllTables();
279         nodeConnectorStats.request();
280         groupStats.request();
281         groupDescStats.request();
282         meterStats.request();
283         meterConfigStats.request();
284         queueStats.request();
285     }
286
287     public synchronized void start(final Timer timer) {
288         flowStats.start(dps);
289         groupDescStats.start(dps);
290         groupStats.start(dps);
291         meterConfigStats.start(dps);
292         meterStats.start(dps);
293         queueStats.start(dps);
294
295         timer.schedule(task, (long) (Math.random() * FIRST_COLLECTION_MILLIS), STATS_COLLECTION_MILLIS);
296
297         logger.debug("Statistics handler for node started with base interval {}ms", STATS_COLLECTION_MILLIS);
298
299         requestPeriodicStatistics();
300     }
301
302     @Override
303     public synchronized void close() {
304         task.cancel();
305         flowStats.close();
306         groupDescStats.close();
307         groupStats.close();
308         meterConfigStats.close();
309         meterStats.close();
310         queueStats.close();
311
312         logger.debug("Statistics handler for {} shut down", targetNodeKey.getId());
313     }
314
315     @Override
316     public void registerTransaction(TransactionId id) {
317         msgManager.recordExpectedTransaction(id);
318         logger.debug("Transaction {} for node {} sent successfully", id, targetNodeKey);
319     }
320
321     @Override
322     public void registerTableTransaction(final TransactionId id, final Short table) {
323         msgManager.recordExpectedTableTransaction(id, table);
324         logger.debug("Transaction {} for node {} table {} sent successfully", id, targetNodeKey, table);
325     }
326 }