1
|
|
|
"""Statistics application.""" |
2
|
|
|
from kytos.core import KytosNApp, log, rest |
3
|
|
|
from kytos.core.helpers import listen_to |
4
|
|
|
from pyof.v0x01.controller2switch.stats_request import StatsType |
5
|
|
|
|
6
|
|
|
from napps.kytos.of_stats import settings |
7
|
|
|
from napps.kytos.of_stats.stats import FlowStats, PortStats |
8
|
|
|
from napps.kytos.of_stats.stats_api import FlowStatsAPI, PortStatsAPI, StatsAPI |
9
|
|
|
|
10
|
|
|
|
11
|
|
|
class Main(KytosNApp): |
12
|
|
|
"""Main class for statistics application.""" |
13
|
|
|
|
14
|
|
|
def setup(self): |
15
|
|
|
"""Initialize all statistics and set their loop interval.""" |
16
|
|
|
self.execute_as_loop(settings.STATS_INTERVAL) |
17
|
|
|
|
18
|
|
|
# Initialize statistics |
19
|
|
|
msg_out = self.controller.buffers.msg_out |
20
|
|
|
self._stats = {StatsType.OFPST_PORT.value: PortStats(msg_out), |
21
|
|
|
StatsType.OFPST_FLOW.value: FlowStats(msg_out)} |
22
|
|
|
|
23
|
|
|
StatsAPI.controller = self.controller |
24
|
|
|
|
25
|
|
|
def execute(self): |
26
|
|
|
"""Query all switches sequentially and then sleep before repeating.""" |
27
|
|
|
switches = list(self.controller.switches.values()) |
28
|
|
|
for switch in switches: |
29
|
|
|
if switch.is_connected(): |
30
|
|
|
self._update_stats(switch) |
31
|
|
|
|
32
|
|
|
def shutdown(self): |
33
|
|
|
"""End of the application.""" |
34
|
|
|
log.debug('Shutting down...') |
35
|
|
|
|
36
|
|
|
def _update_stats(self, switch): |
37
|
|
|
for stats in self._stats.values(): |
38
|
|
|
if switch.connection is not None: |
39
|
|
|
stats.request(switch.connection) |
40
|
|
|
|
41
|
|
|
@listen_to('kytos/of_core.v0x01.messages.in.ofpt_stats_reply') |
42
|
|
|
def listen_v0x01(self, event): |
43
|
|
|
"""Detect the message body type.""" |
44
|
|
|
stats_reply = event.content['message'] |
45
|
|
|
stats_type = stats_reply.body_type |
46
|
|
|
self._listen(event, stats_type) |
47
|
|
|
|
48
|
|
|
@listen_to('kytos/of_core.v0x04.messages.in.ofpt_multipart_reply') |
49
|
|
|
def listen_v0x04(self, event): |
50
|
|
|
"""Detect the message body type.""" |
51
|
|
|
multipart_reply = event.content['message'] |
52
|
|
|
stats_type = multipart_reply.multipart_type |
53
|
|
|
self._listen(event, stats_type) |
54
|
|
|
|
55
|
|
|
def _listen(self, event, stats_type): |
56
|
|
|
"""Listen to all stats reply we deal with. |
57
|
|
|
|
58
|
|
|
Note: v0x01 ``body_type`` and v0x04 ``multipart_type`` have the same |
59
|
|
|
values. Besides, both ``msg.body`` have the fields/attributes we use. |
60
|
|
|
Thus, we can treat them the same way and reuse the code. |
61
|
|
|
""" |
62
|
|
|
msg = event.content['message'] |
63
|
|
|
if stats_type.value in self._stats: |
64
|
|
|
stats = self._stats[stats_type.value] |
65
|
|
|
stats_list = msg.body |
66
|
|
|
stats.listen(event.source.switch, stats_list) |
67
|
|
|
else: |
68
|
|
|
log.debug('No listener for %s = %s in %s.', stats_type.name, |
69
|
|
|
stats_type.value, list(self._stats.keys())) |
70
|
|
|
|
71
|
|
|
# REST API |
72
|
|
|
|
73
|
|
|
@rest('v1/<dpid>/ports/<int:port>') |
74
|
|
|
@staticmethod |
75
|
|
|
def get_port_stats(dpid, port): |
76
|
|
|
"""Return statistics for ``dpid`` and ``port``.""" |
77
|
|
|
return PortStatsAPI.get_port_stats(dpid, port) |
78
|
|
|
|
79
|
|
|
@rest('v1/<dpid>/ports') |
80
|
|
|
@staticmethod |
81
|
|
|
def get_ports_list(dpid): |
82
|
|
|
"""Return ports of ``dpid``.""" |
83
|
|
|
return PortStatsAPI.get_ports_list(dpid) |
84
|
|
|
|
85
|
|
|
@rest('v1/<dpid>/flows/<flow_hash>') |
86
|
|
|
@staticmethod |
87
|
|
|
def get_flow_stats(dpid, flow_hash): |
88
|
|
|
"""Return statistics of a flow in ``dpid``.""" |
89
|
|
|
return FlowStatsAPI.get_flow_stats(dpid, flow_hash) |
90
|
|
|
|
91
|
|
|
@rest('v1/<dpid>/flows') |
92
|
|
|
@staticmethod |
93
|
|
|
def get_flow_list(dpid): |
94
|
|
|
"""Return all flows of ``dpid``.""" |
95
|
|
|
return FlowStatsAPI.get_flow_list(dpid) |
96
|
|
|
|
97
|
|
|
@rest('v1/<dpid>/ports/<int:port>/random') |
98
|
|
|
@staticmethod |
99
|
|
|
def get_random_interface_stats(dpid, port): |
100
|
|
|
"""Fake data for testing.""" |
101
|
|
|
# Ignore dpid and port |
102
|
|
|
# pylint: disable=unused-argument |
103
|
|
|
return PortStatsAPI.get_random_port_stats() |
104
|
|
|
|