1 #!/usr/local/bin/python
8 # 1. Start the controller
9 # 2. On the local machine (e.g., your laptop), start this script.
10 # > python analytics.py
11 # 3. On the mininet VM, run:
12 # > sudo mn --controller=remote,ip=192.168.56.1 --topo tree,2
14 # 4. Give commands to analytics.py. For instance:
15 # > host bytes 10.0.0.1 10.0.0.3
16 # (There is a usage prompt that prints at the beginning of analytics.py)
17 # 5. Type 'quit' to exit analytics.py
21 Class for keeping track of host stats or affinity link stats, depending.
25 # TODO: Each stat should probably be a thread, and handle its
26 # own output and refreshing for the EWMA
28 def __init__(self, stat_type, **kwargs):
29 self.stat_type = stat_type
30 if stat_type == "host":
31 self.src = kwargs['src']
32 self.dst = kwargs['dst']
33 self.url_prefix = "http://localhost:8080/affinity/nb/v2/analytics/default/hoststats/"
34 elif stat_type == "affinityLink":
35 self.al = kwargs['al']
36 self.url_prefix = "http://localhost:8080/affinity/nb/v2/analytics/default/affinitylinkstats/"
38 print "incorrect stat type", stat_type
43 self.http = httplib2.Http(".cache")
44 self.http.add_credentials('admin', 'admin')
49 if (self.stat_type == "host"):
50 resp, content = self.http.request(self.url_prefix + self.src + "/" + self.dst, "GET")
51 elif (self.stat_type == "affinityLink"):
52 resp, content = self.http.request(self.url_prefix + self.al, "GET")
53 self.stats = json.loads(content)
54 self.handle_rate_ewma()
56 # EWMA calculation for bit rate
57 def handle_rate_ewma(self):
59 anomaly_threshold = 2.0
60 new_bitrate = self.get_bit_rate()
62 if self.rate_ewma == None:
63 self.rate_ewma = new_bitrate
65 new_rate_ewma = alpha * new_bitrate + (1 - alpha) * self.rate_ewma
66 if (self.rate_ewma > 0 and new_rate_ewma > anomaly_threshold * self.rate_ewma):
67 if (self.stat_type == "host"):
68 print "Anomaly detected between %s and %s" % (self.src, self.dst)
69 elif (self.stat_type == "affinityLink"):
70 print "Anomaly detected on AffinityLink %s" % (self.al)
71 print "Rate rose from %1.1f mbit/s to %1.1f mbit/s" % ((self.rate_ewma/10**6), (new_rate_ewma/10**6))
72 self.rate_ewma = new_rate_ewma
77 bytes = long(self.stats["byteCount"])
78 except Exception as e:
83 def get_bit_rate(self):
85 bitrate = float(self.stats["bitRate"])
86 except Exception as e:
92 Class for controlling subnets. Right now, just adds subnets and
93 checks whether they exist, because that's all we need.
98 self.http = httplib2.Http(".cache")
99 self.http.add_credentials("admin", "admin")
100 self.url_prefix = "http://localhost:8080/controller/nb/v2/subnetservice/default/"
102 # Checks whether subnet exists. Checks against the actual subnet
103 # string (e.g., "10.0.0.255/1"), not the subnet name. Will not
104 # catch things like overlapping subnets.
105 def exists(self, subnet):
106 resp, content = self.http.request(self.url_prefix + "subnets", "GET")
107 if (resp.status != 200):
108 print "Fatal error - can't check for subnet existence"
110 data = json.loads(content)
112 for key in data["subnetConfig"]:
113 if (key["subnet"] == subnet):
117 # Add a subnet if it doesn't already exist.
118 def add_subnet(self, subnet_name, subnet):
119 if (self.exists(subnet)):
120 print "subnet", subnet, "already exists"
122 subnet_config = dict(name=subnet_name, subnet=subnet)
123 json_data = json.dumps(subnet_config)
124 resp, content = self.http.request(self.url_prefix + "subnet/" + subnet_name, "POST", json_data, {'Content-Type': 'application/json'})
125 if (resp.status == 201):
126 print "subnet", subnet, "added"
128 print "subnet", subnet, "could not be added"
131 def run_interactive_mode():
133 print "Usage: [host | link] [bytes | rate] [src dst | link-name]"
137 request = raw_input("> ")
139 request = request.split()
140 request_type = request[0]
142 if (request_type == "quit"):
147 if (request_type == "host"):
148 src, dst = request[2:4]
149 if (action == "bytes"):
150 host_stat = Stats("host", src=src, dst=dst)
151 print("%d bytes between %s and %s" % (host_stat.get_bytes(), src, dst))
152 elif (action == "rate"):
153 host_stat = Stats("host", src=src, dst=dst)
154 print("%f bit/s between %s and %s" % (host_stat.get_bit_rate(), src, dst))
158 # TODO: Change this to use AffinityLinkStats
159 elif (request_type == "link"):
161 h = httplib2.Http(".cache")
162 h.add_credentials("admin", "admin")
163 resp, content = h.request("http://localhost:8080/affinity/nb/v2/analytics/default/affinitylinkstats/" + link, "GET")
164 al_stats = json.loads(content)
166 if (action == "bytes"):
167 print("%d bytes on %s" % (long(al_stats["byteCount"]), link))
168 elif (action == "rate"):
169 print("%f bit/s on %s" % (float(al_stats["bitRate"]), link))
175 except Exception as e:
181 h = httplib2.Http(".cache")
182 h.add_credentials("admin", "admin")
184 resp, content = h.request("http://localhost:8080/controller/nb/v2/hosttracker/default/hosts/active", "GET")
185 host_content = json.loads(content)
187 # Even if there are no active hosts, host_content['hostConfig']
188 # still exists (and is empty)
190 for host_data in host_content['hostConfig']:
191 active_hosts.append(host_data['networkAddress'])
195 def run_passive_mode():
197 affinity_link_stats = {}
198 affinity_links = set(["testAL"]) # TODO: Get these automatically
201 # Go through all affinity link stats
202 for al in affinity_links:
203 if al not in affinity_link_stats:
204 affinity_link_stats[al] = Stats("affinityLink", al=al)
205 stat = affinity_link_stats[al]
207 print "%d bytes (%1.1f mbit/s) on %s" % (stat.get_bytes(), (stat.get_bit_rate() / (10**6)), al)
213 # Default subnet is required for the host tracker to work. Run
214 # this script once *before* you start mininet.
215 subnet_control = SubnetControl()
216 subnet_control.add_subnet("defaultSubnet", "10.0.0.254/8")
218 interactive_mode = False
221 run_interactive_mode()
225 if __name__ == "__main__":