Passed
Pull Request — master (#30)
by Rogerio
07:01
created

build.main.Main.trace()   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 5
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
ccs 5
cts 5
cp 1
crap 1
1
"""Main module of amlight/sdntrace_cp Kytos Network Application.
2
3
Run tracepaths on OpenFlow in the Control Plane
4
"""
5
6 1
from datetime import datetime
7
8 1
from flask import jsonify, request
9 1
from kytos.core import KytosEvent, KytosNApp, log, rest
10 1
from kytos.core.helpers import listen_to
11 1
from napps.amlight.flow_stats.main import Main as FlowManager
12 1
from napps.amlight.sdntrace_cp import settings
13 1
from napps.amlight.sdntrace_cp.automate import Automate
14 1
from napps.amlight.sdntrace_cp.utils import (convert_entries, find_endpoint,
15
                                             prepare_json)
16
17
18 1
class Main(KytosNApp):
19
    """Main class of amlight/sdntrace_cp NApp.
20
21
    This application gets the list of flows from the switches
22
    and uses it to trace paths without using the data plane.
23
    """
24
25 1
    def setup(self):
26
        """Replace the '__init__' method for the KytosNApp subclass.
27
28
        The setup method is automatically called by the controller when your
29
        application is loaded.
30
31
        """
32 1
        log.info("Starting Kytos SDNTrace CP App!")
33
34 1
        self.traces = {}
35 1
        self.last_id = 30000
36 1
        self.automate = Automate(self)
37 1
        if settings.TRIGGER_SCHEDULE_TRACES:
38
            event = KytosEvent('amlight/scheduler.add_job')
39
            event.content['id'] = 'automatic_traces'
40
            event.content['func'] = self.automate.run_traces
41
            try:
42
                trigger = settings.SCHEDULE_TRIGGER
43
                kwargs = settings.SCHEDULE_ARGS
44
            except AttributeError:
45
                trigger = 'interval'
46
                kwargs = {'seconds': 60}
47
            event.content['kwargs'] = {'trigger': trigger}
48
            event.content['kwargs'].update(kwargs)
49
            self.controller.buffers.app.put(event)
50 1
        if settings.TRIGGER_IMPORTANT_CIRCUITS:
51
            event = KytosEvent('amlight/scheduler.add_job')
52
            event.content['id'] = 'automatic_important_traces'
53
            event.content['func'] = self.automate.run_important_traces
54
            try:
55
                trigger = settings.IMPORTANT_CIRCUITS_TRIGGER
56
                kwargs = settings.IMPORTANT_CIRCUITS_ARGS
57
            except AttributeError:
58
                trigger = 'interval'
59
                kwargs = {'minutes': 10}
60
            event.content['kwargs'] = {'trigger': trigger}
61
            event.content['kwargs'].update(kwargs)
62
            self.controller.buffers.app.put(event)
63
64 1
    def execute(self):
65
        """This method is executed right after the setup method execution.
66
67
        You can also use this method in loop mode if you add to the above setup
68
        method a line like the following example:
69
70
            self.execute_as_loop(30)  # 30-second interval.
71
        """
72
73 1
    def shutdown(self):
74
        """This method is executed when your napp is unloaded.
75
76
        If you have some cleanup procedure, insert it here.
77
        """
78
        event = KytosEvent('amlight/scheduler.remove_job')
79
        event.content['id'] = 'automatic_traces'
80
        self.controller.buffers.app.put(event)
81
        event = KytosEvent('amlight/scheduler.remove_job')
82
        event.content['id'] = 'automatic_important_traces'
83
        self.controller.buffers.app.put(event)
84
85 1
    @rest('/trace', methods=['PUT'])
86 1
    def trace(self):
87
        """Trace a path."""
88 1
        entries = request.get_json()
89 1
        result = self.tracepath(entries)
90 1
        return jsonify(prepare_json(result))
91
92 1
    def tracepath(self, entries):
93
        """Trace a path for a packet represented by entries."""
94 1
        self.last_id += 1
95 1
        trace_id = self.last_id
96 1
        entries = convert_entries(entries)
97 1
        trace_result = []
98 1
        trace_type = 'starting'
99
100 1
        do_trace = True
101 1
        while do_trace:
102 1
            trace_step = {'in': {'dpid': entries['dpid'],
103
                                 'port': entries['in_port'],
104
                                 'time': str(datetime.now()),
105
                                 'type': trace_type}}
106 1
            if 'vlan_vid' in entries:
107 1
                trace_step['in'].update({'vlan': entries['vlan_vid'][-1]})
108 1
            switch = self.controller.get_switch_by_dpid(entries['dpid'])
109 1
            result = self.trace_step(switch, entries)
110 1
            if result:
111 1
                out = {'port': result['out_port']}
112 1
                if 'vlan_vid' in result['entries']:
113
                    out.update({'vlan': result['entries']['vlan_vid'][-1]})
114 1
                trace_step.update({
115
                    'out': out
116
                })
117 1
                if 'dpid' in result:
118 1
                    next_step = {'dpid': result['dpid'],
119
                                 'port': result['in_port']}
120 1
                    if self.has_loop(next_step, trace_result):
121 1
                        do_trace = False
122
                    else:
123 1
                        entries = result['entries']
124 1
                        entries['dpid'] = result['dpid']
125 1
                        entries['in_port'] = result['in_port']
126 1
                        trace_type = 'trace'
127
                else:
128
                    do_trace = False
129
            else:
130 1
                do_trace = False
131 1
            trace_result.append(trace_step)
132 1
        self.traces.update({
133
            trace_id: trace_result
134
        })
135 1
        return trace_result
136
137 1
    @staticmethod
138 1
    def has_loop(trace_step, trace_result):
139
        """Check if there is a loop in the trace result."""
140 1
        for trace in trace_result:
141 1
            if trace['in']['dpid'] == trace_step['dpid'] and \
142
                            trace['in']['port'] == trace_step['port']:
143 1
                return True
144 1
        return False
145
146 1
    @staticmethod
147 1
    def trace_step(switch, entries):
148
        """Perform a trace step.
149
150
        Match the given fields against the switch's list of flows."""
151 1
        flow, entries, port = FlowManager.match_and_apply(switch, entries)
152 1
        if not flow or not port:
153 1
            return None
154
155 1
        endpoint = find_endpoint(switch, port)
156 1
        if endpoint is None:
157 1
            return {'out_port': port,
158
                    'entries': entries}
159
160 1
        return {'dpid': endpoint.switch.dpid,
161
                'in_port': endpoint.port_number,
162
                'out_port': port,
163
                'entries': entries}
164
165 1
    @listen_to('amlight/flow_stats.flows_updated')
166 1
    def update_circuits(self, event):
167
        """Update the list of circuits after a flow change."""
168
        # pylint: disable=unused-argument
169 1
        if settings.FIND_CIRCUITS_IN_FLOWS:
170
            self.automate.find_circuits()
171