2 * Copyright (c) 2013 Plexxi, Inc. All rights reserved.
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
9 package org.opendaylight.affinity.analytics.internal;
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;
19 import java.util.Map.Entry;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
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;
47 public class AnalyticsManager implements IReadServiceListener, IAnalyticsManager {
49 private static final Logger log = LoggerFactory.getLogger(AnalyticsManager.class);
51 private IAffinityManager affinityManager;
52 private IStatisticsManager statisticsManager;
53 private IfIptoHost hostTracker;
55 private Map<MatchField, Host> destinationHostCache;
56 private Map<MatchField, Host> sourceHostCache;
57 private Map<Host, Map<Host, HostStats>> hostsToStats;
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>>();
67 log.debug("DESTROY called!");
71 log.debug("START called!");
78 log.debug("STOP called!");
81 void setAffinityManager(IAffinityManager a) {
82 this.affinityManager = a;
85 void unsetAffinityManager(IAffinityManager a) {
86 if (this.affinityManager.equals(a)) {
87 this.affinityManager = null;
91 void setStatisticsManager(IStatisticsManager s) {
92 this.statisticsManager = s;
95 void unsetStatisticsManager(IStatisticsManager s) {
96 if (this.statisticsManager.equals(s)) {
97 this.statisticsManager = null;
101 void setHostTracker(IfIptoHost h) {
102 this.hostTracker = h;
105 void unsetHostTracker(IfIptoHost h) {
106 if (this.hostTracker.equals(h)) {
107 this.hostTracker = null;
111 /* Returns the destination host associated with this flow, if one
112 * exists. Returns null otherwise.
114 protected Host getDestinationHostFromFlow(Flow flow, Set<HostNodeConnector> hosts) {
115 Match match = flow.getMatch();
116 MatchField dst = null;
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);
128 Host cacheHit = this.destinationHostCache.get(dst);
129 if (cacheHit != null) {
133 // Find the destination host
135 for (HostNodeConnector h : hosts) {
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)) {
143 this.destinationHostCache.put(dst, dstHost); // Add to cache
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)) {
153 this.destinationHostCache.put(dst, dstHost); // Add to cache
162 /* Returns the source Host associated with this flow, if one
163 * exists. Returns null otherwise.
165 protected Host getSourceHostFromFlow(Flow flow, Set<HostNodeConnector> hosts) {
168 Match match = flow.getMatch();
170 // Flow must have IN_PORT field (DL_SRC rarely (never?)
172 if (match.isPresent(MatchType.IN_PORT)) {
173 MatchField inPort = match.getField(MatchType.IN_PORT);
176 Host cacheHit = this.sourceHostCache.get(inPort);
177 if (cacheHit != null) {
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)) {
187 this.sourceHostCache.put(inPort, h); // Add to cache
196 public long getByteCountBetweenHosts(Host src, Host dst) {
199 if (this.hostsToStats.get(src) != null &&
200 this.hostsToStats.get(src).get(dst) != null) {
201 byteCount = this.hostsToStats.get(src).get(dst).getByteCount();
207 public double getBitRateBetweenHosts(Host src, Host dst) {
209 if (this.hostsToStats.get(src) != null &&
210 this.hostsToStats.get(src).get(dst) != null) {
211 bitRate = this.hostsToStats.get(src).get(dst).getBitRate();
217 public double getBitRateOnAffinityLink(AffinityLink al) {
218 // Returns bit rate in *bits-per-second*
219 double maxDuration = 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;
234 if (maxDuration == 0.0) {
237 return (totalBytes * 8.0) / maxDuration;
241 public long getByteCountOnAffinityLink(AffinityLink al) {
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);
253 private InetAddress getPrefix(InetAddress ip, Short mask) {
254 byte[] prefix = ip.getAddress();
255 InetAddress newIP = null;
257 int bits = (32 - mask) % 8;
258 int bytes = 4 - ((int) mask / 8);
262 // zero out the bytes
263 for (int i = 1; i <= bytes; i++) {
264 prefix[prefix.length - i] = 0x0;
268 prefix[prefix.length - bytes - 1] &= (0xFF << bits);
270 newIP = InetAddress.getByAddress(prefix);
271 } catch (UnknownHostException e) {
277 public long getByteCountIntoHost(Host targetHost) {
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();
288 public long getByteCountIntoPrefix(String prefixAndMask, Set <HostNodeConnector> allHosts) {
293 // Split 1.2.3.4/5 format into the prefix (1.2.3.4) and the mask (5)
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);
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);
316 public void nodeFlowStatisticsUpdated(Node node, List<FlowOnNode> flowStatsList) {
317 Set<HostNodeConnector> allHosts = this.hostTracker.getAllHosts();
319 for (FlowOnNode f : flowStatsList) {
320 Host srcHost = getSourceHostFromFlow(f.getFlow(), allHosts);
321 Host dstHost = getDestinationHostFromFlow(f.getFlow(), allHosts);
323 // Source host being null is okay; it indicates that the
324 // source of this particular flow is a switch, not a host.
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());
334 else if (srcHost == null) {
335 log.debug("Source host is null for Flow " + f.getFlow() + ". This is NOT necessarily an error.");
339 if (this.hostsToStats.get(srcHost) == null) {
340 this.hostsToStats.put(srcHost, new HashMap<Host, HostStats>());
342 if (this.hostsToStats.get(srcHost).get(dstHost) == null) {
343 this.hostsToStats.get(srcHost).put(dstHost, new HostStats());
345 this.hostsToStats.get(srcHost).get(dstHost).setStatsFromFlow(f);
350 public void nodeConnectorStatisticsUpdated(Node node, List<NodeConnectorStatistics> ncStatsList) {
351 // Not interested in this update
355 public void nodeTableStatisticsUpdated(Node node, List<NodeTableStatistics> tableStatsList) {
356 // Not interested in this update
360 public void descriptionStatisticsUpdated(Node node, NodeDescription nodeDescription) {
361 // Not interested in this update