build.tracing.tracer   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 193
Duplicated Lines 0 %

Test Coverage

Coverage 97.3%

Importance

Changes 0
Metric Value
eloc 94
dl 0
loc 193
ccs 72
cts 74
cp 0.973
rs 10
c 0
b 0
f 0
wmc 19

7 Methods

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