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