Passed
Pull Request — master (#114)
by Aldo
06:05
created

build.tracing.tracer.TracePath.get_packet_in()   A

Complexity

Conditions 4

Size

Total Lines 10
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 9
nop 1
dl 0
loc 10
ccs 9
cts 9
cp 1
crap 4
rs 9.95
c 0
b 0
f 0
1
"""
2
    Tracer main class
3
"""
4 1
import time
5 1
import queue
6 1
import copy
7 1
from kytos.core import log
8 1
from napps.amlight.sdntrace.tracing.trace_pkt import generate_trace_pkt
9 1
from napps.amlight.sdntrace.tracing.trace_pkt import prepare_next_packet
10 1
from napps.amlight.sdntrace.tracing.rest import FormatRest
11 1
from napps.amlight.sdntrace.backends.of_parser import send_packet_out
12 1
from napps.amlight.sdntrace.shared.switches import Switches
13 1
from napps.amlight.sdntrace.shared.colors import Colors
14
15
16 1
class TracePath(object):
17
    """ Tracer main class - responsible for running traces.
18
    It is composed of two parts:
19
     1) Sending PacketOut messages to switches
20
     2) Reading the pktIn queue with PacketIn received
21
22
    There are a few possibilities of result (except for errors):
23
    - Timeouts ({'trace': 'completed'}) - even positive results end w/
24
        timeouts.
25
    - Loops ({'trace': 'loop'}) - every time an entry is seen twice
26
        in the trace_result queue, we stop
27
28
    Some things to take into consideration:
29
    - we can have parallel traces
30
    - we can have flow rewrite along the path (vlan translation, f.i)
31
    """
32
33 1
    def __init__(self, trace_manager, r_id, initial_entries):
34
        """
35
        Args:
36
            trace_manager: main TraceManager class - needed for
37
            Kytos.controller
38
            r_id: request ID
39
            initial_entries: user entries for trace
40
        """
41 1
        self.switches = Switches()
42 1
        self.trace_mgr = trace_manager
43 1
        self.id = r_id
44 1
        self.init_entries = initial_entries
45
46 1
        self.trace_task = None
47 1
        self.step = 0
48 1
        self.trace_result = []
49 1
        self.trace_ended = False
50 1
        self.init_switch = self.get_init_switch()
51 1
        self.rest = FormatRest()
52
53 1
    def get_init_switch(self):
54
        """Get the Switch class of the switch requested by user
55
56
        Returns:
57
            Switch class
58
        """
59 1
        print(self.init_entries)
60 1
        return Switches().get_switch(self.init_entries.dpid)
61
62 1
    def tracepath(self):
63
        """
64
            Do the trace path
65
            The logic is very simple:
66
            1 - Generate the probe packet using entries provided
67
            2 - Results a result and the packet_in (used to generate new probe)
68
                Possible results: 'timeout' meaning the end of trace
69
                                  or the trace step {'dpid', 'port'}
70
                Some networks do vlan rewriting, so it is important to get the
71
                packetIn msg with the header
72
            3 - If result is a trace step, send PacketOut to the switch that
73
                originated the PacketIn. Repeat till reaching timeout
74
        """
75 1
        log.warning("Starting Trace Path ID: %s" % self.id)
76 1
        entries = copy.deepcopy(self.init_entries)
77 1
        color = Colors().get_switch_color(self.init_switch.dpid)
78 1
        switch = self.init_switch
79
        # Add initial trace step
80 1
        self.rest.add_trace_step(self.trace_result, trace_type='starting',
81
                                 dpid=switch.dpid,
82
                                 port=entries.in_port)
83
        # A loop waiting for 'trace_ended'.
84
        # It changes to True when reaches timeout
85 1
        self.tracepath_loop(entries, color, switch)
86
        # Add final result to trace_results_queue
87 1
        t_result = {"request_id": self.id,
88
                    "result": self.trace_result,
89
                    "start_time": str(self.rest.start_time),
90
                    "total_time": self.rest.get_time(),
91
                    "request": self.init_entries.init_entries}
92 1
        self.trace_mgr.add_result(self.id, t_result)
93 1
        self.clear_trace_pkt_in()
94 1
        print("--99--")
95
96 1
    def tracepath_loop(self, entries, color, switch):
97
        """ This method sends the packet_out per hop, create the result
98
        to be posted via REST.
99
        """
100
        # A loop waiting for 'trace_ended'.
101
        # It changes to True when reaches timeout
102 1
        while not self.trace_ended:
103 1
            in_port, probe_pkt = generate_trace_pkt(entries, color, self.id, self.step)
104 1
            result, packet_in = self.send_trace_probe(switch, in_port,
105
                                                      probe_pkt)
106 1
            self.step += 1
107 1
            if result == 'timeout':
108 1
                self.rest.add_trace_step(self.trace_result, trace_type='last')
109 1
                log.warning("Trace %s: Trace Completed!" % self.id)
110 1
                self.trace_ended = True
111
            else:
112 1
                self.rest.add_trace_step(self.trace_result,
113
                                         trace_type='trace',
114
                                         dpid=result['dpid'],
115
                                         port=result['port'])
116 1
                if self.check_loop():
117 1
                    self.rest.add_trace_step(self.trace_result,
118
                                             trace_type='last',
119
                                             reason='loop')
120 1
                    self.trace_ended = True
121 1
                    break
122
                # If we got here, that means we need to keep going.
123 1
                entries, color, switch = prepare_next_packet(entries, result,
124
                                                             packet_in)
125
126 1
    def send_trace_probe(self, switch, in_port, probe_pkt):
127
        """ This method sends the PacketOut and checks if the
128
        PacketIn was received in 3 seconds.
129
130
        Args:
131
            switch: target switch to start with
132
            in_port: target port to start with
133
            probe_pkt: ethernet frame to send (PacketOut.data)
134
135
        Returns:
136
            Timeout
137
            {switch & port}
138
        """
139 1
        timeout_control = 0  # Controls the timeout of 1 second and two tries
140 1
        while not self.trace_ended:
141 1
            log.warning(f'Trace {self.id}: Sending POut to switch:'
142
                        f' {switch.dpid} and in_port {in_port}.'
143
                        f' Timeout: {self.init_entries.timeout}')
144 1
            send_packet_out(self.trace_mgr.controller,
145
                            switch, in_port, probe_pkt)
146
147 1
            time.sleep(self.init_entries.timeout)
148 1
            pkt_in_msg = self.get_packet_in()
149
150 1
            if pkt_in_msg:
151 1
                result = {"dpid": pkt_in_msg["dpid"],
152
                          "port": pkt_in_msg["in_port"]}
153 1
                return result, pkt_in_msg["event"]
154
155 1
            timeout_control += 1
156 1
            if timeout_control >= 3:
157 1
                return 'timeout', False
158
159 1
    def get_packet_in(self):
160
        """Wait for a PacketIn and verify if it is from the correct step."""
161 1
        while not self.trace_ended:
162 1
            try:
163 1
                pkt_in_msg = self.trace_mgr._trace_pkt_in[self.id].sync_q.get(block=False)
164 1
            except queue.Empty:
165 1
                return None
166 1
            msg = pkt_in_msg["msg"]
167 1
            if msg.step == self.step:
168 1
                return pkt_in_msg
169
170 1
    def clear_trace_pkt_in(self):
171
        """ Once the probe PacketIn was processed, delete it from queue."""
172 1
        print("pkt?? -> ", self.trace_mgr._trace_pkt_in)
173 1
        if self.id in self.trace_mgr._trace_pkt_in:
174
            self.trace_mgr._trace_pkt_in[self.id].close()
175
            del self.trace_mgr._trace_pkt_in[self.id]
176
177 1
    def check_loop(self):
178
        """ Check if there are equal entries
179
180
        Return:
181
            True if loop
182
            0 if not
183
        """
184 1
        last = self.trace_result[-1]
185 1
        for result in self.trace_result[:-1]:
186 1
            if result['dpid'] == last['dpid']:
187 1
                if result['port'] == last['port']:
188 1
                    log.warning('Trace %s: Loop Detected on %s port %s!!' %
189
                                (self.id, last['dpid'], last['port']))
190 1
                    return True
191
        return 0
192