02bc1a64baa3c6d292ba6cfd928f071c4da82e4b
[affinity.git] / scripts / analytics.py
1 #!/usr/local/bin/python
2
3 '''
4 Copyright (c) 2013 Plexxi, Inc.  All rights reserved.
5
6 This program and the accompanying materials are made available under the
7 terms of the Eclipse Public License v1.0 which accompanies this distribution,
8 and is available at http://www.eclipse.org/legal/epl-v10.html
9 '''
10
11 import json
12 import requests
13 import sys
14 import time
15
16 from stats import Stats
17 from subnet import SubnetControl
18 from affinity_control import AffinityControl
19
20 # This class has nothing to do with the analytics API, but it makes
21 # adding flows *so* much easier.
22 class Flow:
23
24     def __init__(self, node, data):
25         self.node_id = node['id']
26         self.priority = data['priority']
27         self.ether_type = '0x800'
28         for match_field in data['match']['matchField']:
29             if match_field['type'] == 'DL_DST':
30                 self.dl_dst = match_field['value']
31             elif match_field['type'] == 'IN_PORT':
32                 self.in_port = match_field['value'].split('@')[0].split('|')[-1]
33         self.output_port = data['actions'][0]['port']['id']
34
35     def set_priority(self, priority):
36         self.priority = priority
37
38     def set_protocol(self, protocol):
39         self.protocol = protocol
40
41     def get_json(self, name):
42         json_data = {'installInHw' : 'true',
43                      'name' : name,
44                      'node' : {'id' : self.node_id, 'type' : 'OF'},
45                      'priority' : self.priority,
46                      'etherType' : self.ether_type,
47                      'dlDst' : self.dl_dst,
48                      'ingressPort' : self.in_port,
49                      'protocol' : self.protocol,
50                      'actions' : ['OUTPUT=%s' % self.output_port]}
51         return json_data
52
53 # Generic REST query
54 def rest_method(url, rest_type, payload=None):
55     if (rest_type == "GET"):
56         resp = requests.get(url, auth=('admin', 'admin'))
57         print "status:", resp.status_code
58         return resp.json()
59     elif (rest_type == "PUT"):
60         headers = {'content-type': 'application/json'}
61         resp = requests.put(url, auth=('admin', 'admin'), data=json.dumps(payload), headers=headers)
62         print "status:", resp.status_code, resp.text
63     elif (rest_type == "DELETE"):
64         resp = requests.delete(url, auth=('admin', 'admin'))
65         print "status:", resp.status_code
66
67
68 ### Host Statistics
69
70 def stats_hosts(src, dst):
71     url = "http://localhost:8080/affinity/nb/v2/analytics/default/hoststats/%s/%s" % (src, dst)
72     data = rest_method(url, "GET")
73     print("%s bytes between %s and %s" % (data["byteCount"], src, dst))
74     print("%s bit/s between %s and %s" % (data["bitRate"], src, dst))
75
76 def stats_hosts_protocol(src, dst, protocol):
77     url = "http://localhost:8080/affinity/nb/v2/analytics/default/hoststats/%s/%s/%d" % (src, dst, protocol)
78     data = rest_method(url, "GET")
79     print("%s bytes between %s and %s for protocol %d" % (data["byteCount"], src, dst, protocol))
80     print("%s bit/s between %s and %s on protocol %d" % (data["bitRate"], src, dst, protocol))
81
82 def all_stats_hosts(src, dst):
83     url = "http://localhost:8080/affinity/nb/v2/analytics/default/hoststats/%s/%s/all" % (src, dst)
84     data = rest_method(url, "GET")['stats']
85     for entry in data:
86         print("%s bytes from protocol %s" % (entry['stat']['byteCount'], entry['protocol']))
87         print("%s bit/s from protocol %s" % (entry['stat']['bitRate'], entry['protocol']))
88
89 ### Affinity link statistics
90
91 def stats_link(al):
92     url = "http://localhost:8080/affinity/nb/v2/analytics/default/affinitylinkstats/%s" % al
93     data = rest_method(url, "GET")
94     print("%s bytes on link %s" % (data['byteCount'], al))
95     print("%s bit/s on link %s" % (data['bitRate'], al))
96
97 def stats_link_protocol(al, protocol):
98     url = "http://localhost:8080/affinity/nb/v2/analytics/default/affinitylinkstats/%s/%s" % (al, protocol)
99     data = rest_method(url, "GET")
100     print("%s bytes on link %s for protocol %s" % (data['byteCount'], al, protocol))
101     print("%s bit/s on link %s for protocol %s" % (data['bitRate'], al, protocol))
102
103 def all_stats_link(al):
104     url = "http://localhost:8080/affinity/nb/v2/analytics/default/affinitylinkstats/%s/all" % al
105     data = rest_method(url, "GET")['stats']
106     for entry in data:
107         print("%s bytes from protocol %s" % (entry['stat']['byteCount'], entry['protocol']))
108         print("%s bit/s from protocol %s" % (entry['stat']['bitRate'], entry['protocol']))
109
110 ### Subnet statistics
111
112 def stats_subnet(src_sub, dst_sub):
113     url = "http://localhost:8080/affinity/nb/v2/analytics/default/subnetstats/%s/%s" % (src_sub, dst_sub)
114     data = rest_method(url, "GET")
115     if (src_sub == "null/null"):
116         print("%s bytes into %s" % (data['byteCount'], dst_sub))
117         print("%s bit/s into %s" % (data['bitRate'], dst_sub))
118     elif (dst_sub == "null/null"):
119         print("%s bytes out of %s" % (data['byteCount'], src_sub))
120         print("%s bit/s out of %s" % (data['bitRate'], src_sub))
121     else:
122         print("%s bytes between %s and %s" % (data['byteCount'], src_sub, dst_sub))
123         print("%s bit/s between %s and %s" % (data['bitRate'], src_sub, dst_sub))
124
125 def stats_subnet_protocol(src_sub, dst_sub, protocol):
126     url = "http://localhost:8080/affinity/nb/v2/analytics/default/subnetstats/%s/%s/%s" % (src_sub, dst_sub, protocol)
127     data = rest_method(url, "GET")
128     if (src_sub == "null/null"):
129         print("%s bytes into %s from protocol %s" % (data['byteCount'], dst_sub, protocol))
130         print("%s bit/s into %s from protocol %s" % (data['bitRate'], dst_sub, protocol))
131     elif (dst_sub == "null/null"):
132         print("%s bytes out of %s from protocol %s" % (data['byteCount'], src_sub, protocol))
133         print("%s bit/s out of %s from protocol %s" % (data['bitRate'], src_sub, protocol))
134     else:
135         print("%s bytes between %s and %s from protocol %s" % (data['byteCount'], src_sub, dst_sub, protocol))
136         print("%s bit/s between %s and %s from protocol %s" % (data['bitRate'], src_sub, dst_sub, protocol))
137
138 def all_stats_subnet(src_sub, dst_sub):
139     url = "http://localhost:8080/affinity/nb/v2/analytics/default/subnetstats/%s/%s/all" % (src_sub, dst_sub)
140     data = rest_method(url, "GET")['stats']
141     for entry in data:
142         print("%s bytes from protocol %s" % (entry['stat']['byteCount'], entry['protocol']))
143         print("%s bit/s from protocol %s" % (entry['stat']['bitRate'], entry['protocol']))
144
145 def incoming_hosts(subnet):
146     url = "http://localhost:8080/affinity/nb/v2/analytics/default/subnetstats/incoming/%s" % subnet
147     data = rest_method(url, "GET")
148     data = rest_method(url, "GET")['stats']
149     if (type(data) == type({})):
150         data = [data]
151     for entry in data:
152         print("%s bytes from host %s" % (entry['byteCount'], entry['hostIP']))
153
154 def incoming_hosts_protocol(subnet, protocol):
155     url = "http://localhost:8080/affinity/nb/v2/analytics/default/subnetstats/incoming/%s/%s" % (subnet, protocol)
156     data = rest_method(url, "GET")['data']['entry']
157     if (type(data) == type({})):
158         data = [data]
159     for entry in data:
160         print("%s bytes from host %s" % (entry['byteCount'], entry['hostIP']))
161
162 # This is not part of the analytics NB API, but it is a necessary step
163 # if you want to monitor protocols
164 def add_protocol_flows():
165     protocols = [1, 6, 17] # ICMP, TCP, UDP
166     flows = get_flows()
167     i = 0
168     for flow in flows:
169         i += 1
170         name = "flow" + str(i)
171         flow.set_priority(2)
172         flow.set_protocol(1)
173         add_flow(flow, name)
174
175 #### Flow control methods
176
177 def get_flows():
178     url = "http://localhost:8080/controller/nb/v2/statistics/default/flow"
179     data = rest_method(url, "GET")
180     flows = []
181     for item in data['flowStatistics']:
182         n = item['node']
183         for item in item['flowStatistic']:
184             f = Flow(n, item['flow'])
185             flows.append(f)
186     return flows
187
188 def add_flow(flow, flow_name):
189     print "adding flow %s" % flow_name
190     url = "http://localhost:8080/controller/nb/v2/flowprogrammer/default/node/OF/%s/staticFlow/%s" % (flow.node_id, flow_name)
191     rest_method(url, "PUT", flow.get_json(flow_name))
192
193 #### End flow control methods
194
195 def run_interactive_mode():
196
197     print "Usage: [host | link | subnet] [src dst | link-name | src_sub dst_sub] {protocol}"
198
199     # Demo mode
200     while True:
201
202         request = raw_input("> ")
203         request = request.split()
204         request_type = request[0]
205
206         if (request_type == "quit" or request_type == "exit"):
207             sys.exit()
208
209         if (request_type == "host"):
210             if (len(request) == 3):
211                 src, dst = request[1:3]
212                 stats_hosts(src, dst)
213                 all_stats_hosts(src, dst)
214             elif (len(request) == 4):
215                 src, dst, protocol = request[1:4]
216                 stats_hosts_protocol(src, dst, int(protocol))
217
218         elif (request_type == "link"):
219             if (len(request) == 2):
220                 link = request[1]
221                 stats_link(link)
222                 all_stats_link(link)
223             elif (len(request) == 3):
224                 link, protocol = request[1:3]
225                 stats_link_protocol(link, protocol)
226                 
227         elif (request_type == "subnet"):
228             if (len(request) == 3):
229                 src_sub, dst_sub = request[1:3]
230                 if src_sub == "null": src_sub = "null/null"
231                 if dst_sub == "null": dst_sub = "null/null"
232                 stats_subnet(src_sub, dst_sub)
233                 all_stats_subnet(src_sub, dst_sub)
234             elif (len(request) == 4):
235                 src_sub, dst_sub, protocol = request[1:4]
236                 if src_sub == "null": src_sub = "null/null"
237                 if dst_sub == "null": dst_sub = "null/null"
238                 stats_subnet_protocol(src_sub, dst_sub, protocol)
239
240 def main():
241
242     # Default subnet is required for the host tracker to work.
243     subnet_control = SubnetControl()
244     subnet_control.add_subnet("defaultSubnet", "10.0.0.254/8")
245
246     # Set up an affinity link
247     affinity_control = AffinityControl()
248     affinity_control.add_affinity_group("testAG1", ips=["10.0.0.1", "10.0.0.2"])
249     affinity_control.add_affinity_group("testAG2", ips=["10.0.0.3", "10.0.0.4"])
250     affinity_control.add_affinity_link("testAL", "testAG1", "testAG2")
251
252     raw_input("press enter ")
253     add_protocol_flows()
254
255     run_interactive_mode()
256
257 if __name__ == "__main__":
258     main()
259