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 // TODO: get "analytics" somewhere in this namespace
48 //import org.opendaylight.yang.gen.v1.urn.opendaylight.affinity.rev131016.HostStatistics;
50 public class AnalyticsManager implements IReadServiceListener, IAnalyticsManager {
52 private static final Logger log = LoggerFactory.getLogger(AnalyticsManager.class);
54 private IAffinityManager affinityManager;
55 private IStatisticsManager statisticsManager;
56 private IfIptoHost hostTracker;
58 private Map<MatchField, Host> destinationHostCache;
59 private Map<MatchField, Host> sourceHostCache;
60 private Map<Host, Map<Host, HostStats>> hostsToStats;
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>>();
70 log.debug("DESTROY called!");
74 log.debug("START called!");
81 log.debug("STOP called!");
84 void setAffinityManager(IAffinityManager a) {
85 this.affinityManager = a;
88 void unsetAffinityManager(IAffinityManager a) {
89 if (this.affinityManager.equals(a)) {
90 this.affinityManager = null;
94 void setStatisticsManager(IStatisticsManager s) {
95 this.statisticsManager = s;
98 void unsetStatisticsManager(IStatisticsManager s) {
99 if (this.statisticsManager.equals(s)) {
100 this.statisticsManager = null;
104 void setHostTracker(IfIptoHost h) {
105 this.hostTracker = h;
108 void unsetHostTracker(IfIptoHost h) {
109 if (this.hostTracker.equals(h)) {
110 this.hostTracker = null;
114 /* Returns the destination host associated with this flow, if one
115 * exists. Returns null otherwise.
117 protected Host getDestinationHostFromFlow(Flow flow, Set<HostNodeConnector> hosts) {
118 Match match = flow.getMatch();
119 MatchField dst = null;
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);
131 Host cacheHit = this.destinationHostCache.get(dst);
132 if (cacheHit != null) {
136 // Find the destination host
138 for (HostNodeConnector h : hosts) {
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)) {
146 this.destinationHostCache.put(dst, dstHost); // Add to cache
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)) {
156 this.destinationHostCache.put(dst, dstHost); // Add to cache
165 /* Returns the source Host associated with this flow, if one
166 * exists. Returns null otherwise.
168 protected Host getSourceHostFromFlow(Flow flow, Set<HostNodeConnector> hosts) {
171 Match match = flow.getMatch();
173 // Flow must have IN_PORT field (DL_SRC rarely (never?)
175 if (match.isPresent(MatchType.IN_PORT)) {
176 MatchField inPort = match.getField(MatchType.IN_PORT);
179 Host cacheHit = this.sourceHostCache.get(inPort);
180 if (cacheHit != null) {
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)) {
190 this.sourceHostCache.put(inPort, h); // Add to cache
199 public long getByteCountBetweenHosts(Host src, Host dst) {
202 if (this.hostsToStats.get(src) != null &&
203 this.hostsToStats.get(src).get(dst) != null) {
204 byteCount = this.hostsToStats.get(src).get(dst).getByteCount();
210 public double getBitRateBetweenHosts(Host src, Host dst) {
212 if (this.hostsToStats.get(src) != null &&
213 this.hostsToStats.get(src).get(dst) != null) {
214 bitRate = this.hostsToStats.get(src).get(dst).getBitRate();
220 public double getBitRateOnAffinityLink(AffinityLink al) {
221 // Returns bit rate in *bits-per-second*
222 double maxDuration = 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;
237 if (maxDuration == 0.0) {
240 return (totalBytes * 8.0) / maxDuration;
244 public long getByteCountOnAffinityLink(AffinityLink al) {
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);
256 private InetAddress getPrefix(InetAddress ip, Short mask) {
257 byte[] prefix = ip.getAddress();
258 InetAddress newIP = null;
260 int bits = (32 - mask) % 8;
261 int bytes = 4 - ((int) mask / 8);
265 // zero out the bytes
266 for (int i = 1; i <= bytes; i++) {
267 prefix[prefix.length - i] = 0x0;
271 prefix[prefix.length - bytes - 1] &= (0xFF << bits);
273 newIP = InetAddress.getByAddress(prefix);
274 } catch (UnknownHostException e) {
280 public long getByteCountIntoHost(Host targetHost) {
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();
291 public long getByteCountIntoPrefix(String prefixAndMask, Set <HostNodeConnector> allHosts) {
296 // Split 1.2.3.4/5 format into the prefix (1.2.3.4) and the mask (5)
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);
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);
319 public void nodeFlowStatisticsUpdated(Node node, List<FlowOnNode> flowStatsList) {
320 Set<HostNodeConnector> allHosts = this.hostTracker.getAllHosts();
322 for (FlowOnNode f : flowStatsList) {
323 Host srcHost = getSourceHostFromFlow(f.getFlow(), allHosts);
324 Host dstHost = getDestinationHostFromFlow(f.getFlow(), allHosts);
326 // Source host being null is okay; it indicates that the
327 // source of this particular flow is a switch, not a host.
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());
337 else if (srcHost == null) {
338 log.debug("Source host is null for Flow " + f.getFlow() + ". This is NOT necessarily an error.");
342 if (this.hostsToStats.get(srcHost) == null) {
343 this.hostsToStats.put(srcHost, new HashMap<Host, HostStats>());
345 if (this.hostsToStats.get(srcHost).get(dstHost) == null) {
346 this.hostsToStats.get(srcHost).put(dstHost, new HostStats());
348 this.hostsToStats.get(srcHost).get(dstHost).setStatsFromFlow(f);
353 public void nodeConnectorStatisticsUpdated(Node node, List<NodeConnectorStatistics> ncStatsList) {
354 // Not interested in this update
358 public void nodeTableStatisticsUpdated(Node node, List<NodeTableStatistics> tableStatsList) {
359 // Not interested in this update
363 public void descriptionStatisticsUpdated(Node node, NodeDescription nodeDescription) {
364 // Not interested in this update