Add rate-based tracking of flow.
[affinity.git] / analytics / implementation / src / main / java / org / opendaylight / affinity / analytics / internal / AnalyticsManager.java
1 /*
2  * Copyright (c) 2013 Plexxi, Inc.  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.affinity.analytics.internal;
10
11 import java.net.InetAddress;
12 import java.net.UnknownHostException;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Map.Entry;
19 import java.util.Set;
20
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 import org.opendaylight.affinity.affinity.AffinityGroup;
25 import org.opendaylight.affinity.affinity.AffinityLink;
26 import org.opendaylight.affinity.affinity.IAffinityManager;
27 import org.opendaylight.affinity.analytics.IAnalyticsManager;
28 import org.opendaylight.controller.hosttracker.IfIptoHost;
29 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
30 import org.opendaylight.controller.sal.core.Host;
31 import org.opendaylight.controller.sal.core.Node;
32 import org.opendaylight.controller.sal.core.NodeConnector;
33 import org.opendaylight.controller.sal.flowprogrammer.Flow;
34 import org.opendaylight.controller.sal.match.Match;
35 import org.opendaylight.controller.sal.match.MatchField;
36 import org.opendaylight.controller.sal.match.MatchType;
37 import org.opendaylight.controller.sal.packet.address.EthernetAddress;
38 import org.opendaylight.controller.sal.reader.FlowOnNode;
39 import org.opendaylight.controller.sal.reader.IReadServiceListener;
40 import org.opendaylight.controller.sal.reader.NodeConnectorStatistics;
41 import org.opendaylight.controller.sal.reader.NodeDescription;
42 import org.opendaylight.controller.sal.reader.NodeTableStatistics;
43 import org.opendaylight.controller.sal.utils.Status;
44 import org.opendaylight.controller.statisticsmanager.IStatisticsManager;
45
46 public class AnalyticsManager implements IReadServiceListener, IAnalyticsManager {
47
48     private static final Logger log = LoggerFactory.getLogger(AnalyticsManager.class);
49
50     private IAffinityManager affinityManager;
51     private IStatisticsManager statisticsManager;
52     private IfIptoHost hostTracker;
53
54     private Map<MatchField, Host> destinationHostCache;
55     private Map<MatchField, Host> sourceHostCache;
56     private Map<Host, Map<Host, HostStats>> hostsToStats;
57
58     void init() {
59         log.debug("INIT called!");
60         this.destinationHostCache = new HashMap<MatchField, Host>();
61         this.sourceHostCache = new HashMap<MatchField, Host>();
62         this.hostsToStats = new HashMap<Host, Map<Host, HostStats>>();
63     }
64
65     void destroy() {
66         log.debug("DESTROY called!");
67     }
68
69     void start() {
70         log.debug("START called!");
71     }
72
73     void started(){
74     }
75
76     void stop() {
77         log.debug("STOP called!");
78     }
79
80     void setAffinityManager(IAffinityManager a) {
81         this.affinityManager = a;
82
83         // TODO: Testing
84         AffinityGroup ag1 = new AffinityGroup("testAG1");
85         ag1.add("10.0.0.1");
86         ag1.add("10.0.0.2");
87         AffinityGroup ag2 = new AffinityGroup("testAG2");
88         ag2.add("10.0.0.3");
89         ag2.add("10.0.0.4");
90         this.affinityManager.addAffinityGroup(ag1);
91         this.affinityManager.addAffinityGroup(ag2);
92         AffinityLink al = new AffinityLink("testAL", ag1, ag2);
93         this.affinityManager.addAffinityLink(al);
94         // TODO: End testing
95     }
96
97     void unsetAffinityManager(IAffinityManager a) {
98         if (this.affinityManager.equals(a)) {
99             this.affinityManager = null;
100         }
101     }
102
103     void setStatisticsManager(IStatisticsManager s) {
104         this.statisticsManager = s;
105     }
106
107     void unsetStatisticsManager(IStatisticsManager s) {
108         if (this.statisticsManager.equals(s)) {
109             this.statisticsManager = null;
110         }
111     }
112
113     void setHostTracker(IfIptoHost h) {
114         this.hostTracker = h;
115     }
116
117     void unsetHostTracker(IfIptoHost h) {
118         if (this.hostTracker.equals(h)) {
119             this.hostTracker = null;
120         }
121     }
122
123     /* Returns the destination host associated with this flow, if one
124      * exists.  Returns null otherwise.
125      */
126     protected Host getDestinationHostFromFlow(Flow flow, Set<HostNodeConnector> hosts) {
127         Match match = flow.getMatch();
128         MatchField dst = null;
129
130         // Flow has to have DL_DST field or NW_DST field to proceed
131         if (match.isPresent(MatchType.DL_DST)) {
132             dst = match.getField(MatchType.DL_DST);
133         } else if (match.isPresent(MatchType.NW_DST)) {
134             dst = match.getField(MatchType.NW_DST);
135         } else { 
136             return null;
137         }
138
139         // Check cache
140         Host cacheHit = this.destinationHostCache.get(dst);
141         if (cacheHit != null) {
142             return cacheHit;
143         }
144
145         // Find the destination host
146         Host dstHost = null;
147         for (HostNodeConnector h : hosts) {
148             
149             // DL_DST => compare on MAC address strings
150             if (match.isPresent(MatchType.DL_DST)) {
151                 String dstMac = MatchType.DL_DST.stringify(dst.getValue());
152                 String hostMac = ((EthernetAddress) h.getDataLayerAddress()).getMacAddress();
153                 if (dstMac.equals(hostMac)) {
154                     dstHost = h;
155                     this.destinationHostCache.put(dst, dstHost); // Add to cache
156                     break;
157                 }
158             }
159           
160             // NW_DST => compare on IP address (of type InetAddress)
161             else if (match.isPresent(MatchType.NW_DST)) {
162                 InetAddress hostIP = h.getNetworkAddress();
163                 if (dst.getValue().equals(hostIP)) {
164                     dstHost = h;
165                     this.destinationHostCache.put(dst, dstHost); // Add to cache
166                     break;
167                 }
168             }
169         }
170
171         return dstHost;
172     }
173
174     /* Returns the source Host associated with this flow, if one
175      * exists.  Returns null otherwise.
176      */
177     protected Host getSourceHostFromFlow(Flow flow, Set<HostNodeConnector> hosts) {
178
179         Host srcHost = null;
180         Match match = flow.getMatch();
181
182         // Flow must have IN_PORT field (DL_SRC rarely (never?)
183         // exists).
184         if (match.isPresent(MatchType.IN_PORT)) {
185             MatchField inPort = match.getField(MatchType.IN_PORT);
186
187             // Check cache
188             Host cacheHit = this.sourceHostCache.get(inPort);
189             if (cacheHit != null) {
190                 return cacheHit;
191             }
192
193             // Find the source host by comparing the NodeConnectors
194             NodeConnector inPortNc = (NodeConnector) inPort.getValue();
195             for (HostNodeConnector h : hosts) {
196                 NodeConnector hostNc = h.getnodeConnector();
197                 if (hostNc.equals(inPortNc)) {
198                     srcHost = h;
199                     this.sourceHostCache.put(inPort, h); // Add to cache
200                     break;
201                 }
202             }
203         }
204
205         return srcHost;
206     }
207
208     public long getByteCountBetweenHosts(Host src, Host dst) {
209
210         long byteCount = 0;
211         if (this.hostsToStats.get(src) != null &&
212             this.hostsToStats.get(src).get(dst) != null) {
213             byteCount = this.hostsToStats.get(src).get(dst).getByteCount();
214         }
215
216         return byteCount;
217     }
218
219     public double getBitRateBetweenHosts(Host src, Host dst) {
220         double bitRate = 0;
221         if (this.hostsToStats.get(src) != null &&
222             this.hostsToStats.get(src).get(dst) != null) {
223             bitRate = this.hostsToStats.get(src).get(dst).getBitRate();
224         }
225
226         return bitRate;
227     }
228
229     public double getBitRateOnAffinityLink(AffinityLink al) {
230         // Returns bit rate in *bits-per-second*
231         double maxDuration = 0;
232         int totalBytes = 0;
233         List<Entry<Host, Host>> flows = this.affinityManager.getAllFlowsByHost(al);
234         for (Entry<Host, Host> flow : flows) {
235             Host h1 = flow.getKey();
236             Host h2 = flow.getValue();
237             if (this.hostsToStats.get(h1) != null &&
238                 this.hostsToStats.get(h1).get(h2) != null) {
239                 totalBytes += getByteCountBetweenHosts(h1, h2);
240                 double duration = this.hostsToStats.get(h1).get(h2).getDuration();
241                 if (duration > maxDuration) {
242                     maxDuration = duration;
243                 }
244             }
245         }
246         if (maxDuration == 0.0) {
247             return 0.0;
248         } else {
249             return (totalBytes * 8.0) / maxDuration;
250         }
251     }
252
253     public long getByteCountOnAffinityLink(AffinityLink al) {
254         long b = 0;
255         List<Entry<Host, Host>> flows = this.affinityManager.getAllFlowsByHost(al);
256         for (Entry<Host, Host> flow : flows) {
257             Host h1 = flow.getKey();
258             Host h2 = flow.getValue();
259             b += getByteCountBetweenHosts(h1, h2);
260         }
261
262         return b;
263     }
264
265     @Override
266     public void nodeFlowStatisticsUpdated(Node node, List<FlowOnNode> flowStatsList) {
267         Set<HostNodeConnector> allHosts = this.hostTracker.getAllHosts();
268
269         for (FlowOnNode f : flowStatsList) {
270             Host srcHost = getSourceHostFromFlow(f.getFlow(), allHosts);
271             Host dstHost = getDestinationHostFromFlow(f.getFlow(), allHosts);
272
273             if (srcHost == null || dstHost == null) {
274                 log.debug("Error: source or destination is null in nodeFlowStatisticsUpdated");
275                 continue;
276             }
277
278             if (this.hostsToStats.get(srcHost) == null) {
279                 this.hostsToStats.put(srcHost, new HashMap<Host, HostStats>());
280             }
281             if (this.hostsToStats.get(srcHost).get(dstHost) == null) {
282                 this.hostsToStats.get(srcHost).put(dstHost, new HostStats());
283             }
284             this.hostsToStats.get(srcHost).get(dstHost).setStatsFromFlow(f);
285         }
286     }
287
288     @Override
289     public void nodeConnectorStatisticsUpdated(Node node, List<NodeConnectorStatistics> ncStatsList) {
290         // Not interested in this update
291     }
292
293     @Override
294     public void nodeTableStatisticsUpdated(Node node, List<NodeTableStatistics> tableStatsList) {
295         // Not interested in this update
296     }
297
298     @Override
299     public void descriptionStatisticsUpdated(Node node, NodeDescription nodeDescription) {
300         // Not interested in this update
301     }
302 }