bug 537 - Node Reconciliation
[controller.git] / opendaylight / md-sal / statistics-manager / src / main / java / org / opendaylight / controller / md / statistics / manager / FlowStatsTracker.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.math.BigInteger;
11 import java.util.Collection;
12 import java.util.Collections;
13 import java.util.Map.Entry;
14
15 import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
16 import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
17 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
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.FlowCookieMapping;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.nodes.node.table.FlowCookieMap;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.nodes.node.table.FlowCookieMapBuilder;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.nodes.node.table.FlowCookieMapKey;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsData;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsDataBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAggregateFlowStatisticsFromFlowTableForAllFlowsInputBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowsStatisticsFromAllFlowTablesInputBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableInputBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsService;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapList;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapListBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.statistics.FlowStatisticsBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.GenericStatistics;
40 import org.opendaylight.yangtools.yang.binding.DataObject;
41 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
42 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 import com.google.common.base.Optional;
47
48 final class FlowStatsTracker extends AbstractListeningStatsTracker<FlowAndStatisticsMapList, FlowStatsEntry> {
49     private static final Logger LOG = LoggerFactory.getLogger(FlowStatsTracker.class);
50     private static final String ALIEN_SYSTEM_FLOW_ID = "#UF$TABLE*";
51     private final OpendaylightFlowStatisticsService flowStatsService;
52     private FlowTableStatsTracker flowTableStats;
53     private int unaccountedFlowsCounter = 1;
54
55
56     FlowStatsTracker(final OpendaylightFlowStatisticsService flowStatsService, final FlowCapableContext context) {
57         super(context);
58         this.flowStatsService = flowStatsService;
59     }
60     FlowStatsTracker(final OpendaylightFlowStatisticsService flowStatsService, final FlowCapableContext context, final FlowTableStatsTracker flowTableStats) {
61         this(flowStatsService, context);
62         this.flowTableStats = flowTableStats;
63     }
64
65     @Override
66     protected void cleanupSingleStat(final DataModificationTransaction trans, final FlowStatsEntry item) {
67         KeyedInstanceIdentifier<Flow, FlowKey> flowRef = getNodeIdentifier()
68                 .augmentation(FlowCapableNode.class)
69                 .child(Table.class, new TableKey(item.getTableId()))
70                 .child(Flow.class, item.getFlow().getKey());
71         trans.removeOperationalData(flowRef);
72     }
73
74     @Override
75     protected FlowStatsEntry updateSingleStat(final DataModificationTransaction trans, final FlowAndStatisticsMapList map) {
76         short tableId = map.getTableId();
77
78         FlowStatisticsDataBuilder flowStatisticsData = new FlowStatisticsDataBuilder();
79
80         FlowBuilder flowBuilder = new FlowBuilder(map);
81         if (map.getFlowId() != null) {
82             flowBuilder.setId(new FlowId(map.getFlowId().getValue()));
83         }
84         if (map.getFlowId() != null) {
85             flowBuilder.setKey(new FlowKey(new FlowId(map.getKey().getFlowId().getValue())));
86         }
87
88         Flow flowRule = flowBuilder.build();
89
90         FlowAndStatisticsMapListBuilder stats = new FlowAndStatisticsMapListBuilder();
91         stats.setByteCount(map.getByteCount());
92         stats.setPacketCount(map.getPacketCount());
93         stats.setDuration(map.getDuration());
94
95         GenericStatistics flowStats = stats.build();
96
97         //Augment the data to the flow node
98
99         FlowStatisticsBuilder flowStatistics = new FlowStatisticsBuilder();
100         flowStatistics.setByteCount(flowStats.getByteCount());
101         flowStatistics.setPacketCount(flowStats.getPacketCount());
102         flowStatistics.setDuration(flowStats.getDuration());
103
104         flowStatisticsData.setFlowStatistics(flowStatistics.build());
105
106         LOG.debug("Flow : {}",flowRule.toString());
107         LOG.debug("Statistics to augment : {}",flowStatistics.build().toString());
108
109         InstanceIdentifier<Table> tableRef = getNodeIdentifierBuilder()
110                 .augmentation(FlowCapableNode.class)
111                 .child(Table.class, new TableKey(tableId)).toInstance();
112
113         final FlowCookie flowCookie = flowRule.getCookie() != null
114                 ? flowRule.getCookie() : new FlowCookie(BigInteger.ZERO);
115         final InstanceIdentifier<FlowCookieMap> flowCookieRef = tableRef
116                 .augmentation(FlowCookieMapping.class)
117                 .child(FlowCookieMap.class, new FlowCookieMapKey(flowCookie));
118
119         FlowCookieMap cookieMap = (FlowCookieMap) trans.readOperationalData(flowCookieRef);
120
121         /* find flowKey in FlowCookieMap from DataStore/OPERATIONAL */
122         Optional<FlowKey> flowKey = this.getExistFlowKey(flowRule, tableRef, trans, cookieMap);
123         if ( ! flowKey.isPresent()) {
124             /* DataStore/CONFIG For every first statistic needs to be created */
125             flowKey = this.getFlowKeyFromExistFlow(flowRule, tableRef, trans);
126             if ( ! flowKey.isPresent()) {
127                 /* Alien flow */
128                 flowKey = this.makeAlienFlowKey(flowRule);
129             }
130             cookieMap = applyNewFlowKey(cookieMap, flowKey, flowCookie);
131             trans.putOperationalData(flowCookieRef, cookieMap);
132         }
133
134         InstanceIdentifier<Flow> flowRef = getNodeIdentifierBuilder()
135                 .augmentation(FlowCapableNode.class)
136                 .child(Table.class, new TableKey(tableId))
137                 .child(Flow.class, flowKey.get()).toInstance();
138         flowBuilder.setKey(flowKey.get());
139         flowBuilder.addAugmentation(FlowStatisticsData.class, flowStatisticsData.build());
140
141         // Update entry with timestamp of latest response
142         flowBuilder.setKey(flowKey.get());
143         FlowStatsEntry flowStatsEntry = new FlowStatsEntry(tableId, flowBuilder.build());
144         trans.putOperationalData(flowRef, flowBuilder.build());
145         return flowStatsEntry;
146     }
147
148     @Override
149     protected InstanceIdentifier<?> listenPath() {
150         return getNodeIdentifierBuilder().augmentation(FlowCapableNode.class).child(Table.class).child(Flow.class).build();
151     }
152
153     @Override
154     protected String statName() {
155         return "Flow";
156     }
157
158     @Override
159     public void request() {
160         // FIXME: it does not make sense to trigger this before sendAllFlowTablesStatisticsRequest()
161         //        comes back -- we do not have any tables anyway.
162         final Collection<TableKey> tables = flowTableStats.getTables();
163         LOG.debug("Node {} supports {} table(s)", this.getNodeRef(), tables.size());
164         for (final TableKey key : tables) {
165             LOG.debug("Send aggregate stats request for flow table {} to node {}", key.getId(), this.getNodeRef());
166             this.requestAggregateFlows(key);
167         }
168
169         this.requestAllFlowsAllTables();
170
171     }
172     public void requestAllFlowsAllTables() {
173         if (flowStatsService != null) {
174             final GetAllFlowsStatisticsFromAllFlowTablesInputBuilder input = new GetAllFlowsStatisticsFromAllFlowTablesInputBuilder();
175             input.setNode(getNodeRef());
176
177             requestHelper(flowStatsService.getAllFlowsStatisticsFromAllFlowTables(input.build()));
178         }
179     }
180
181     public void requestAggregateFlows(final TableKey key) {
182         if (flowStatsService != null) {
183             GetAggregateFlowStatisticsFromFlowTableForAllFlowsInputBuilder input =
184                     new GetAggregateFlowStatisticsFromFlowTableForAllFlowsInputBuilder();
185
186             input.setNode(getNodeRef());
187             input.setTableId(new org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableId(key.getId()));
188             requestHelper(flowStatsService.getAggregateFlowStatisticsFromFlowTableForAllFlows(input.build()));
189         }
190     }
191
192     public void requestFlow(final Flow flow) {
193         if (flowStatsService != null) {
194             final GetFlowStatisticsFromFlowTableInputBuilder input =
195                     new GetFlowStatisticsFromFlowTableInputBuilder(flow);
196             input.setNode(getNodeRef());
197
198             requestHelper(flowStatsService.getFlowStatisticsFromFlowTable(input.build()));
199         }
200     }
201
202     @Override
203     public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
204         for (Entry<InstanceIdentifier<?>, DataObject> e : change.getCreatedConfigurationData().entrySet()) {
205             if (Flow.class.equals(e.getKey().getTargetType())) {
206                 final Flow flow = (Flow) e.getValue();
207                 LOG.debug("Key {} triggered request for flow {}", e.getKey(), flow);
208                 requestFlow(flow);
209             } else {
210                 LOG.debug("Ignoring key {}", e.getKey());
211             }
212         }
213
214         final DataModificationTransaction trans = startTransaction();
215         for (InstanceIdentifier<?> key : change.getRemovedConfigurationData()) {
216             if (Flow.class.equals(key.getTargetType())) {
217                 @SuppressWarnings("unchecked")
218                 final InstanceIdentifier<Flow> flow = (InstanceIdentifier<Flow>)key;
219                 LOG.debug("Key {} triggered remove of Flow from operational space.", key);
220                 trans.removeOperationalData(flow);
221             }
222         }
223         trans.commit();
224     }
225
226     @Override
227     public void start(final DataBrokerService dbs) {
228         if (flowStatsService == null) {
229             LOG.debug("No Flow Statistics service, not subscribing to flows on node {}", getNodeIdentifier());
230             return;
231         }
232
233         super.start(dbs);
234     }
235
236     /* Returns Exist FlowKey from exist FlowCookieMap identified by cookie
237      * and by switch flow identification (priority and match)*/
238     private Optional<FlowKey> getExistFlowKey(final Flow flowRule, final InstanceIdentifier<Table> tableRef,
239             final DataModificationTransaction trans, final FlowCookieMap cookieMap) {
240
241         if (cookieMap != null) {
242             for (FlowId flowId : cookieMap.getFlowIds()) {
243                 InstanceIdentifier<Flow> flowIdent = tableRef.child(Flow.class, new FlowKey(flowId));
244                 if (flowId.getValue().startsWith(ALIEN_SYSTEM_FLOW_ID)) {
245                     LOG.debug("Search for flow in the operational datastore by flowID: {} ", flowIdent);
246                     Flow readedFlow = (Flow) trans.readOperationalData(flowIdent);
247                     if (FlowComparator.flowEquals(flowRule, readedFlow)) {
248                         return Optional.<FlowKey> of(new FlowKey(flowId));
249                     }
250                 } else {
251                     LOG.debug("Search for flow in the configuration datastore by flowID: {} ", flowIdent);
252                     Flow readedFlow = (Flow) trans.readConfigurationData(flowIdent);
253                     if (FlowComparator.flowEquals(flowRule, readedFlow)) {
254                         return Optional.<FlowKey> of(new FlowKey(flowId));
255                     }
256                 }
257             }
258             LOG.debug("Flow was not found in the datastore. Flow {} ", flowRule);
259         }
260         return Optional.absent();
261     }
262
263     /* Returns FlowKey from existing Flow in DataStore/CONFIGURATIONAL which is identified by cookie
264      * and by switch flow identification (priority and match) */
265     private Optional<FlowKey> getFlowKeyFromExistFlow(final Flow flowRule, final InstanceIdentifier<Table> tableRef,
266             final DataModificationTransaction trans) {
267
268         /* Try to find it in DataSotre/CONFIG */
269         Table table= (Table)trans.readConfigurationData(tableRef);
270         if(table != null) {
271             for(Flow existingFlow : table.getFlow()) {
272                 LOG.debug("Existing flow in data store : {}",existingFlow.toString());
273                 if(FlowComparator.flowEquals(flowRule,existingFlow)){
274                     return Optional.<FlowKey> of(new FlowKey(existingFlow.getId()));
275                 }
276             }
277         }
278         return Optional.absent();
279     }
280
281     /* Returns FlowKey which doesn't exist in any DataStore for now */
282     private Optional<FlowKey> makeAlienFlowKey(final Flow flowRule) {
283
284         StringBuilder sBuilder = new StringBuilder(ALIEN_SYSTEM_FLOW_ID)
285             .append(flowRule.getTableId()).append("-").append(this.unaccountedFlowsCounter);
286         this.unaccountedFlowsCounter++;
287         final FlowId flowId = new FlowId(sBuilder.toString());
288         return Optional.<FlowKey> of(new FlowKey(flowId));
289     }
290
291     /* Build new whole FlowCookieMap or add new flowKey */
292     private FlowCookieMap applyNewFlowKey(FlowCookieMap flowCookieMap, final Optional<FlowKey> flowKey,
293             final FlowCookie flowCookie) {
294         if (flowCookieMap != null) {
295             flowCookieMap.getFlowIds().add(flowKey.get().getId());
296         } else {
297             final FlowCookieMapBuilder flowCookieMapBuilder = new FlowCookieMapBuilder();
298             flowCookieMapBuilder.setCookie(flowCookie);
299             flowCookieMapBuilder.setFlowIds(Collections.singletonList(flowKey.get().getId()));
300             flowCookieMap = flowCookieMapBuilder.build();
301         }
302         return flowCookieMap;
303     }
304 }