Passed
Pull Request — master (#36)
by Vinicius
02:46
created

TestLoopManager.test_handle_loop_detected()   A

Complexity

Conditions 1

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 17
nop 2
dl 0
loc 26
rs 9.55
c 0
b 0
f 0
1
"""Test LoopManager methods."""
2
from datetime import timedelta
3
from unittest import TestCase
4
from unittest.mock import MagicMock, patch
5
6
from kytos.lib.helpers import get_interface_mock, get_switch_mock
7
8
from kytos.core.helpers import now
9
from napps.kytos.of_lldp.loop_manager import LoopManager
10
11
12
class TestLoopManager(TestCase):
13
    """Tests for LoopManager."""
14
15
    def setUp(self):
16
        """Execute steps before each tests."""
17
        controller = MagicMock()
18
        self.loop_manager = LoopManager(controller)
19
20
    def test_is_looped(self):
21
        """Test is_looped cases."""
22
23
        dpid_a = "00:00:00:00:00:00:00:01"
24
        dpid_b = "00:00:00:00:00:00:00:02"
25
        values = [
26
            (dpid_a, 6, dpid_a, 7, True),
27
            (dpid_a, 1, dpid_a, 2, True),
28
            (dpid_a, 7, dpid_a, 7, True),
29
            (dpid_a, 8, dpid_a, 1, False),  # port_a > port_b
30
            (dpid_a, 1, dpid_b, 2, False),
31
            (dpid_a, 2, dpid_b, 1, False),
32
        ]
33
        for dpid_a, port_a, dpid_b, port_b, looped in values:
34
            with self.subTest(
35
                dpid_a=dpid_a, port_a=port_a, port_b=port_b, looped=looped
36
            ):
37
                assert (
38
                    self.loop_manager.is_looped(
39
                        dpid_a, port_a, dpid_b, port_b
40
                    )
41
                    == looped
42
                )
43
44
    def test_is_loop_ignored(self):
45
        """Test is_loop_ignored."""
46
47
        dpid = "00:00:00:00:00:00:00:01"
48
        port_a = 1
49
        port_b = 2
50
        self.loop_manager.ignored_loops[dpid] = [[port_a, port_b]]
51
52
        assert self.loop_manager.is_loop_ignored(
53
            dpid, port_a=port_a, port_b=port_b
54
        )
55
        assert self.loop_manager.is_loop_ignored(
56
            dpid, port_a=port_b, port_b=port_a
57
        )
58
59
        assert not self.loop_manager.is_loop_ignored(
60
            dpid, port_a + 20, port_b
61
        )
62
63
        dpid = "00:00:00:00:00:00:00:02"
64
        assert not self.loop_manager.is_loop_ignored(dpid, port_a, port_b)
65
66
    @patch("napps.kytos.of_lldp.loop_manager.log")
67
    def test_handle_log_action(self, mock_log):
68
        """Test handle_log_action."""
69
70
        dpid = "00:00:00:00:00:00:00:01"
71
        switch = get_switch_mock(dpid, 0x04)
72
        intf_a = get_interface_mock("s1-eth1", 1, switch)
73
        intf_b = get_interface_mock("s1-eth2", 2, switch)
74
75
        self.loop_manager.handle_log_action(intf_a, intf_b)
76
        mock_log.warning.call_count = 1
77
        assert self.loop_manager.loop_counter[dpid][(1, 2)] == 0
78
        self.loop_manager.handle_log_action(intf_a, intf_b)
79
        mock_log.warning.call_count = 1
80
        assert self.loop_manager.loop_counter[dpid][(1, 2)] == 1
81
82
    @patch("napps.kytos.of_lldp.loop_manager.requests")
83
    @patch("napps.kytos.of_lldp.loop_manager.log")
84
    def test_handle_disable_action(self, mock_log, mock_requests):
85
        """Test handle_disable_action."""
86
87
        dpid = "00:00:00:00:00:00:00:01"
88
        switch = get_switch_mock(dpid, 0x04)
89
        intf_a = get_interface_mock("s1-eth1", 1, switch)
90
        intf_b = get_interface_mock("s1-eth2", 2, switch)
91
92
        response = MagicMock()
93
        response.status_code = 200
94
        mock_requests.post.return_value = response
95
        self.loop_manager.handle_disable_action(intf_a, intf_b)
96
        assert mock_requests.post.call_count == 1
97
        assert mock_log.info.call_count == 1
98
99
    @patch("napps.kytos.of_lldp.loop_manager.requests")
100
    @patch("napps.kytos.of_lldp.loop_manager.log")
101
    def test_handle_loop_stopped(self, mock_log, mock_requests):
102
        """Test handle_loop_stopped."""
103
104
        dpid = "00:00:00:00:00:00:00:01"
105
        switch = get_switch_mock(dpid, 0x04)
106
        intf_a = get_interface_mock("s1-eth1", 1, switch)
107
        intf_b = get_interface_mock("s1-eth2", 2, switch)
108
109
        self.loop_manager.loop_state[dpid][(1, 2)] = {"state": "detected"}
110
        response = MagicMock()
111
        response.status_code = 200
112
        mock_requests.post.return_value = response
113
        self.loop_manager.handle_loop_stopped(intf_a, intf_b)
114
        assert mock_requests.delete.call_count == 1
115
        assert "log" in self.loop_manager.actions
116
        assert mock_log.info.call_count == 1
117
        assert self.loop_manager.loop_state[dpid][(1, 2)]["state"] == "stopped"
118
119
    @patch(
120
        "napps.kytos.of_lldp.loop_manager.LoopManager.add_interface_metadata"
121
    )
122
    def test_handle_loop_detected(self, mock_add_interface_metadata):
123
        """Test handle_loop_detected."""
124
125
        dpid = "00:00:00:00:00:00:00:01"
126
        switch = get_switch_mock(dpid, 0x04)
127
        intf_a = get_interface_mock("s1-eth1", 1, switch)
128
129
        port_pair = (1, 2)
130
        self.loop_manager.handle_loop_detected(intf_a.id, dpid, port_pair)
131
        assert (
132
            self.loop_manager.loop_state[dpid][port_pair]["state"]
133
            == "detected"
134
        )
135
        assert mock_add_interface_metadata.call_count == 1
136
137
        # aditional call while is still active it shouldn't add metadata again
138
        self.loop_manager.handle_loop_detected(intf_a.id, dpid, port_pair)
139
        assert mock_add_interface_metadata.call_count == 1
140
141
        # force a different initial state to ensure it would overwrite
142
        self.loop_manager.loop_state[dpid][port_pair]["state"] = "stopped"
143
        self.loop_manager.handle_loop_detected(intf_a.id, dpid, port_pair)
144
        assert mock_add_interface_metadata.call_count == 2
145
146
    @patch("napps.kytos.of_lldp.loop_manager.requests")
147
    def test_add_interface_metadata(self, mock_requests):
148
        """Test add interface metadata."""
149
        dpid = "00:00:00:00:00:00:00:01"
150
        switch = get_switch_mock(dpid, 0x04)
151
        intf_a = get_interface_mock("s1-eth1", 1, switch)
152
        metadata = {
153
            "state": "looped",
154
            "port_numbers": [1, 2],
155
            "updated_at": now().strftime("%Y-%m-%dT%H:%M:%S"),
156
            "detected_at": now().strftime("%Y-%m-%dT%H:%M:%S"),
157
        }
158
        self.loop_manager.add_interface_metadata(intf_a.id, metadata)
159
        assert mock_requests.post.call_count == 1
160
161
    @patch("napps.kytos.of_lldp.loop_manager.requests")
162
    def test_del_interface_metadata(self, mock_requests):
163
        """Test del interface metadata."""
164
        dpid = "00:00:00:00:00:00:00:01"
165
        switch = get_switch_mock(dpid, 0x04)
166
        intf_a = get_interface_mock("s1-eth1", 1, switch)
167
        self.loop_manager.del_interface_metadata(intf_a.id, "looped")
168
        assert mock_requests.delete.call_count == 1
169
170
    def test_publish_loop_state(self):
171
        """Test publish_loop_state."""
172
        dpid = "00:00:00:00:00:00:00:01"
173
        switch = get_switch_mock(dpid, 0x04)
174
        intf_a = get_interface_mock("s1-eth1", 1, switch)
175
        intf_b = get_interface_mock("s1-eth2", 2, switch)
176
        state = "detected"
177
178
        self.loop_manager.publish_loop_state(intf_a, intf_b, state)
179
        assert self.loop_manager.controller.buffers.app.put.call_count == 1
180
181
    def test_publish_loop_actions(self):
182
        """Test publish_loop_actions."""
183
        dpid = "00:00:00:00:00:00:00:01"
184
        switch = get_switch_mock(dpid, 0x04)
185
        intf_a = get_interface_mock("s1-eth1", 1, switch)
186
        intf_b = get_interface_mock("s1-eth2", 2, switch)
187
        self.loop_manager.publish_loop_actions(intf_a, intf_b)
188
        assert self.loop_manager.controller.buffers.app.put.call_count == len(
189
            set(self.loop_manager.actions)
190
        )
191
192
    def test_get_stopped_loops(self):
193
        """Test get_stopped_loops."""
194
        dpid = "00:00:00:00:00:00:00:01"
195
        port_pairs = [(1, 2), (3, 3)]
196
197
        delta = now() - timedelta(minutes=1)
198
        looped_entry = {
199
            "state": "detected",
200
            "updated_at": delta.strftime("%Y-%m-%dT%H:%M:%S"),
201
        }
202
        for port_pair in port_pairs:
203
            self.loop_manager.loop_state[dpid][port_pair] = looped_entry
204
        assert self.loop_manager.get_stopped_loops() == {dpid: port_pairs}
205
206
    @patch("napps.kytos.of_lldp.loop_manager.LoopManager.publish_loop_state")
207
    @patch("napps.kytos.of_lldp.loop_manager.LoopManager.publish_loop_actions")
208
    def test_process_if_looped(self, mock_publish_actions, mock_publish_state):
209
        """Test process_if_looped."""
210
        dpid = "00:00:00:00:00:00:00:01"
211
        switch = get_switch_mock(dpid, 0x04)
212
        intf_a = get_interface_mock("s1-eth1", 1, switch)
213
        intf_b = get_interface_mock("s1-eth2", 2, switch)
214
        self.loop_manager.ignored_loops = {}
215
        assert self.loop_manager.process_if_looped(intf_a, intf_b)
216
        assert mock_publish_actions.call_count == 1
217
        assert mock_publish_state.call_count == 1
218
219
    def test_handle_topology_update(self):
220
        """Test handle_topology pudate."""
221
        dpid = "00:00:00:00:00:00:00:01"
222
        switch = get_switch_mock(dpid, 0x04)
223
        mock_topo = MagicMock()
224
        switch.metadata = {"ignored_loops": [[1, 2]]}
225
        mock_topo.switches = {dpid: switch}
226
227
        assert dpid not in self.loop_manager.ignored_loops
228
        self.loop_manager.handle_topology_loaded(mock_topo)
229
        assert self.loop_manager.ignored_loops[dpid] == [[1, 2]]
230
231
    def test_handle_switch_metadata_changed_added(self):
232
        """Test handle_switch_metadata_changed added."""
233
        dpid = "00:00:00:00:00:00:00:01"
234
        switch = get_switch_mock(dpid, 0x04)
235
        switch.metadata = {"ignored_loops": [[1, 2]]}
236
237
        assert dpid not in self.loop_manager.ignored_loops
238
        self.loop_manager.handle_switch_metadata_changed(switch)
239
        assert self.loop_manager.ignored_loops[dpid] == [[1, 2]]
240
241
    def test_handle_switch_metadata_changed_removed(self):
242
        """Test handle_switch_metadata_changed removed."""
243
        dpid = "00:00:00:00:00:00:00:01"
244
        switch = get_switch_mock(dpid, 0x04)
245
        switch.id = dpid
246
        switch.metadata = {"some_key": "some_value"}
247
        self.loop_manager.ignored_loops[dpid] = [[1, 2]]
248
249
        assert dpid in self.loop_manager.ignored_loops
250
        self.loop_manager.handle_switch_metadata_changed(switch)
251
        assert dpid not in self.loop_manager.ignored_loops
252