ac7c152f4bb508c218510a0e70f51f7b2b34a667
[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.lang.Short;
12 import java.net.InetAddress;
13 import java.net.UnknownHostException;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import java.util.Set;
21
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 import org.opendaylight.affinity.affinity.AffinityGroup;
26 import org.opendaylight.affinity.affinity.AffinityLink;
27 import org.opendaylight.affinity.affinity.IAffinityManager;
28 import org.opendaylight.affinity.analytics.IAnalyticsManager;
29 import org.opendaylight.controller.hosttracker.IfIptoHost;
30 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
31 import org.opendaylight.controller.sal.core.Host;
32 import org.opendaylight.controller.sal.core.Node;
33 import org.opendaylight.controller.sal.core.NodeConnector;
34 import org.opendaylight.controller.sal.flowprogrammer.Flow;
35 import org.opendaylight.controller.sal.match.Match;
36 import org.opendaylight.controller.sal.match.MatchField;
37 import org.opendaylight.controller.sal.match.MatchType;
38 import org.opendaylight.controller.sal.packet.address.EthernetAddress;
39 import org.opendaylight.controller.sal.reader.FlowOnNode;
40 import org.opendaylight.controller.sal.reader.IReadServiceListener;
41 import org.opendaylight.controller.sal.reader.NodeConnectorStatistics;
42 import org.opendaylight.controller.sal.reader.NodeDescription;
43 import org.opendaylight.controller.sal.reader.NodeTableStatistics;
44 import org.opendaylight.controller.sal.utils.Status;
45 import org.opendaylight.controller.statisticsmanager.IStatisticsManager;
46
47 public class AnalyticsManager implements IReadServiceListener, IAnalyticsManager {
48
49     private static final Logger log = LoggerFactory.getLogger(AnalyticsManager.class);
50
51     private IAffinityManager affinityManager;
52     private IStatisticsManager statisticsManager;
53     private IfIptoHost hostTracker;
54
55     private Map<MatchField, Host> destinationHostCache;
56     private Map<MatchField, Host> sourceHostCache;
57     private Map<Host, Map<Host, HostStats>> hostsToStats;
58
59     void init() {
60         log.debug("INIT called!");
61         this.destinationHostCache = new HashMap<MatchField, Host>();
62         this.sourceHostCache = new HashMap<MatchField, Host>();
63         this.hostsToStats = new HashMap<Host, Map<Host, HostStats>>();
64     }
65
66     void destroy() {
67         log.debug("DESTROY called!");
68     }
69
70     void start() {
71         log.debug("START called!");
72     }
73
74     void started(){
75     }
76
77     void stop() {
78         log.debug("STOP called!");
79     }
80
81     void setAffinityManager(IAffinityManager a) {
82         this.affinityManager = a;
83     }
84
85     void unsetAffinityManager(IAffinityManager a) {
86         if (this.affinityManager.equals(a)) {
87             this.affinityManager = null;
88         }
89     }
90
91     void setStatisticsManager(IStatisticsManager s) {
92         this.statisticsManager = s;
93     }
94
95     void unsetStatisticsManager(IStatisticsManager s) {
96         if (this.statisticsManager.equals(s)) {
97             this.statisticsManager = null;
98         }
99     }
100
101     void setHostTracker(IfIptoHost h) {
102         this.hostTracker = h;
103     }
104
105     void unsetHostTracker(IfIptoHost h) {
106         if (this.hostTracker.equals(h)) {
107             this.hostTracker = null;
108         }
109     }
110
111     /* Returns the destination host associated with this flow, if one
112      * exists.  Returns null otherwise.
113      */
114     protected Host getDestinationHostFromFlow(Flow flow, Set<HostNodeConnector> hosts) {
115         Match match = flow.getMatch();
116         MatchField dst = null;
117
118         // Flow has to have DL_DST field or NW_DST field to proceed
119         if (match.isPresent(MatchType.DL_DST)) {
120             dst = match.getField(MatchType.DL_DST);
121         } else if (match.isPresent(MatchType.NW_DST)) {
122             dst = match.getField(MatchType.NW_DST);
123         } else { 
124             return null;
125         }
126
127         // Check cache
128         Host cacheHit = this.destinationHostCache.get(dst);
129         if (cacheHit != null) {
130             return cacheHit;
131         }
132
133         // Find the destination host
134         Host dstHost = null;
135         for (HostNodeConnector h : hosts) {
136             
137             // DL_DST => compare on MAC address strings
138             if (match.isPresent(MatchType.DL_DST)) {
139                 String dstMac = MatchType.DL_DST.stringify(dst.getValue());
140                 String hostMac = ((EthernetAddress) h.getDataLayerAddress()).getMacAddress();
141                 if (dstMac.equals(hostMac)) {
142                     dstHost = h;
143                     this.destinationHostCache.put(dst, dstHost); // Add to cache
144                     break;
145                 }
146             }
147           
148             // NW_DST => compare on IP address (of type InetAddress)
149             else if (match.isPresent(MatchType.NW_DST)) {
150                 InetAddress hostIP = h.getNetworkAddress();
151                 if (dst.getValue().equals(hostIP)) {
152                     dstHost = h;
153                     this.destinationHostCache.put(dst, dstHost); // Add to cache
154                     break;
155                 }
156             }
157         }
158
159         return dstHost;
160     }
161
162     /* Returns the source Host associated with this flow, if one
163      * exists.  Returns null otherwise.
164      */
165     protected Host getSourceHostFromFlow(Flow flow, Set<HostNodeConnector> hosts) {
166
167         Host srcHost = null;
168         Match match = flow.getMatch();
169
170         // Flow must have IN_PORT field (DL_SRC rarely (never?)
171         // exists).
172         if (match.isPresent(MatchType.IN_PORT)) {
173             MatchField inPort = match.getField(MatchType.IN_PORT);
174
175             // Check cache
176             Host cacheHit = this.sourceHostCache.get(inPort);
177             if (cacheHit != null) {
178                 return cacheHit;
179             }
180
181             // Find the source host by comparing the NodeConnectors
182             NodeConnector inPortNc = (NodeConnector) inPort.getValue();
183             for (HostNodeConnector h : hosts) {
184                 NodeConnector hostNc = h.getnodeConnector();
185                 if (hostNc.equals(inPortNc)) {
186                     srcHost = h;
187                     this.sourceHostCache.put(inPort, h); // Add to cache
188                     break;
189                 }
190             }
191         }
192
193         return srcHost;
194     }
195
196     public long getByteCountBetweenHosts(Host src, Host dst) {
197
198         long byteCount = 0;
199         if (this.hostsToStats.get(src) != null &&
200             this.hostsToStats.get(src).get(dst) != null) {
201             byteCount = this.hostsToStats.get(src).get(dst).getByteCount();
202         }
203
204         return byteCount;
205     }
206
207     public double getBitRateBetweenHosts(Host src, Host dst) {
208         double bitRate = 0;
209         if (this.hostsToStats.get(src) != null &&
210             this.hostsToStats.get(src).get(dst) != null) {
211             bitRate = this.hostsToStats.get(src).get(dst).getBitRate();
212         }
213
214         return bitRate;
215     }
216
217     public double getBitRateOnAffinityLink(AffinityLink al) {
218         // Returns bit rate in *bits-per-second*
219         double maxDuration = 0;
220         int totalBytes = 0;
221         List<Entry<Host, Host>> flows = this.affinityManager.getAllFlowsByHost(al);
222         for (Entry<Host, Host> flow : flows) {
223             Host h1 = flow.getKey();
224             Host h2 = flow.getValue();
225             if (this.hostsToStats.get(h1) != null &&
226                 this.hostsToStats.get(h1).get(h2) != null) {
227                 totalBytes += getByteCountBetweenHosts(h1, h2);
228                 double duration = this.hostsToStats.get(h1).get(h2).getDuration();
229                 if (duration > maxDuration) {
230                     maxDuration = duration;
231                 }
232             }
233         }
234         if (maxDuration == 0.0) {
235             return 0.0;
236         } else {
237             return (totalBytes * 8.0) / maxDuration;
238         }
239     }
240
241     public long getByteCountOnAffinityLink(AffinityLink al) {
242         long b = 0;
243         List<Entry<Host, Host>> flows = this.affinityManager.getAllFlowsByHost(al);
244         for (Entry<Host, Host> flow : flows) {
245             Host h1 = flow.getKey();
246             Host h2 = flow.getValue();
247             b += getByteCountBetweenHosts(h1, h2);
248         }
249
250         return b;
251     }
252
253     private InetAddress getPrefix(InetAddress ip, Short mask) {
254         byte[] prefix = ip.getAddress();
255         InetAddress newIP = null;
256         try {
257             int bits = (32 - mask) % 8;
258             int bytes = 4 - ((int) mask / 8);
259             if (bits > 0) {
260                 bytes--;
261             }
262             // zero out the bytes
263             for (int i = 1; i <= bytes; i++) {
264                 prefix[prefix.length - i] = 0x0;
265             }
266             // zero out the bits
267             if (bits > 0) {
268                 prefix[prefix.length - bytes - 1] &= (0xFF << bits);
269             }
270             newIP = InetAddress.getByAddress(prefix);
271         } catch (UnknownHostException e) {
272             // TODO:
273         }
274         return newIP;
275     }
276
277     public long getByteCountIntoHost(Host targetHost) {
278         long totalBytes = 0;
279         // We're calculating bytes *into* the target host, not out of
280         for (Host sourceHost : this.hostsToStats.keySet()) {
281             if (this.hostsToStats.get(sourceHost).get(targetHost) != null) {
282                 totalBytes += this.hostsToStats.get(sourceHost).get(targetHost).getByteCount();
283             }
284         }
285         return totalBytes;
286     }
287
288     public long getByteCountIntoPrefix(String prefixAndMask, Set <HostNodeConnector> allHosts) {
289         long totalBytes = 0;
290         InetAddress ip;
291         Short mask;
292
293         // Split 1.2.3.4/5 format into the prefix (1.2.3.4) and the mask (5)
294         try {
295             String[] splitPrefix = prefixAndMask.split("/");
296             ip = InetAddress.getByName(splitPrefix[0]);
297             mask = (splitPrefix.length == 2) ? Short.valueOf(splitPrefix[1]) : 32;
298         } catch (UnknownHostException e) {
299             log.debug("Incorrect prefix/mask format: " + prefixAndMask);
300             return 0;
301         }
302
303         // Match on prefixes
304         InetAddress targetPrefix = getPrefix(ip, mask);
305         for (HostNodeConnector host : allHosts) {
306             InetAddress hostPrefix = getPrefix(host.getNetworkAddress(), mask);
307             if (hostPrefix.equals(targetPrefix)) {
308                 totalBytes += getByteCountIntoHost(host);
309             }
310         }
311
312         return totalBytes;
313     }
314
315     @Override
316     public void nodeFlowStatisticsUpdated(Node node, List<FlowOnNode> flowStatsList) {
317         Set<HostNodeConnector> allHosts = this.hostTracker.getAllHosts();
318
319         for (FlowOnNode f : flowStatsList) {
320             Host srcHost = getSourceHostFromFlow(f.getFlow(), allHosts);
321             Host dstHost = getDestinationHostFromFlow(f.getFlow(), allHosts);
322
323             // Source host being null is okay; it indicates that the
324             // source of this particular flow is a switch, not a host.
325             //
326             // TODO: It would be useful, at least for debugging
327             // output, to differentiate between when the source is a
328             // switch and when it's a host that the hosttracker
329             // doesn't know about. The latter would be an error.
330             if (dstHost == null) {
331                 log.debug("Error: Destination host is null for Flow " + f.getFlow());
332                 continue;
333             }
334             else if (srcHost == null) {
335                 log.debug("Source host is null for Flow " + f.getFlow() + ". This is NOT necessarily an error.");
336                 continue;
337             }
338
339             if (this.hostsToStats.get(srcHost) == null) {
340                 this.hostsToStats.put(srcHost, new HashMap<Host, HostStats>());
341             }
342             if (this.hostsToStats.get(srcHost).get(dstHost) == null) {
343                 this.hostsToStats.get(srcHost).put(dstHost, new HostStats());
344             }
345             this.hostsToStats.get(srcHost).get(dstHost).setStatsFromFlow(f);
346         }
347     }
348
349     @Override
350     public void nodeConnectorStatisticsUpdated(Node node, List<NodeConnectorStatistics> ncStatsList) {
351         // Not interested in this update
352     }
353
354     @Override
355     public void nodeTableStatisticsUpdated(Node node, List<NodeTableStatistics> tableStatsList) {
356         // Not interested in this update
357     }
358
359     @Override
360     public void descriptionStatisticsUpdated(Node node, NodeDescription nodeDescription) {
361         // Not interested in this update
362     }
363 }