Passed
Pull Request — master (#255)
by Aldo
03:54
created

build.tests.unit.test_main   F

Complexity

Total Complexity 111

Size/Duplication

Total Lines 2251
Duplicated Lines 8.84 %

Test Coverage

Coverage 89.51%

Importance

Changes 0
Metric Value
eloc 1767
dl 199
loc 2251
ccs 1340
cts 1497
cp 0.8951
rs 0.8
c 0
b 0
f 0
wmc 111

1 Function

Rating   Name   Duplication   Size   Complexity  
A test_handle_link_liveness_status() 0 19 1

86 Methods

Rating   Name   Duplication   Size   Complexity  
A TestMain.setup_method() 0 10 2
A TestMain.test_get_event_listeners() 0 23 1
A TestMain.test_get_link_or_create() 0 22 1
A TestMain.test_handle_new_switch() 0 11 1
A TestMain.test_set_special_tags() 0 40 1
A TestMain.test_handle_link_up_intf_down() 0 18 1
A TestMain.test_set_tag_range_tag_error() 20 20 1
A TestMain.test_handle_on_interface_tags() 0 18 1
A TestMain.test_delete_interface() 0 10 1
A TestMain.test_notify_port_created() 0 9 1
A TestMain.test_disable_switch() 0 33 2
A TestMain.test_delete_tag_range_type_error() 0 15 1
A TestMain.test_notify_topology_update() 0 6 1
A TestMain.test_disable_link() 0 24 2
A TestMain.test_add_switch_metadata() 0 25 1
A TestMain.test_get_tag_ranges_by_intf_error() 0 9 1
A TestMain.test_get_switch_metadata() 0 17 1
A TestMain.test_get_link_from_interface() 0 14 1
B TestMain.test_get_topology() 0 51 1
A TestMain.test_add_links() 0 24 1
A TestMain.test_notify_link_status_change() 0 32 1
A TestMain.test_handle_link_down() 0 15 1
A TestMain.test_interface_link_up_unordered_event() 0 15 1
A TestMain.test_set_tag_range_not_found() 0 13 1
B TestMain.test_enable_link() 0 40 8
A TestMain.test_get_links_from_interfaces() 0 16 1
A TestMain.test_handle_interfaces_created() 0 17 1
A TestMain.test_interface_link_down_unordered_event() 0 15 1
A TestMain.test_handle_interface_down() 0 8 1
B TestMain.test_load_switch() 0 59 1
B TestMain.test_load_topology() 0 65 3
A TestMain.test_handle_link_down_not_active_last_status() 0 14 1
A TestMain.test_notify_switch_links_status() 0 22 1
A TestMain.test_delete_tag_range() 15 15 1
A TestMain.test_set_tag_range() 0 23 1
A TestMain.test_link_status_hook_link_up_timer() 0 18 1
B TestMain.test_interface_link_up() 0 50 1
A TestMain.test_add_link_metadata_wrong_format() 0 12 1
A TestMain.test_enable_switch() 0 23 1
A TestMain.test_delete_interface_metadata() 0 41 1
A TestMain.test_add_interface_metadata() 0 30 1
A TestMain.test_load_topology_fail_link() 0 19 1
A TestMain.test_delete_tag_range_not_found() 14 14 1
A TestMain.test_handle_interface_created_inactive() 0 13 1
A TestMain.test_interruption_end() 39 39 1
A TestMain.test_get_link_or_create_mismatched() 0 30 1
B TestMain.test_enable_interfaces() 0 66 3
A TestMain.test_add_switch_metadata_wrong_format() 0 13 1
A TestMain.test_delete_link() 0 41 1
A TestMain.test_interruption_start() 39 39 1
A TestMain.test_notify_interface_link_status() 0 18 1
A TestMain.test_load_interfaces_tags_values() 0 24 1
A TestMain.test_notify_switch_enabled() 0 7 1
A TestMain.test_get_interface_metadata() 0 26 1
A TestMain.test_handle_link_up() 0 17 1
A TestMain.test_load_topology_fail_switch() 0 19 1
A TestMain.test_get_all_tag_ranges() 24 24 1
A TestMain.test_delete_switch_metadata() 0 35 1
A TestMain.test_get_intf_usage() 0 24 1
A TestMain.test_notify_link_status() 0 10 1
A TestMain.test_handle_topo_controller_upsert_switch() 0 6 1
A TestMain.test_interface_deleted() 0 6 1
A TestMain.test_notify_link_up_if_status() 0 33 1
A TestMain.test_delete_interface_api() 0 36 1
B TestMain.test_load_switch_attrs() 0 68 1
B TestMain.test_disable_interfaces() 0 70 3
A TestMain.test_set_tag_range_type_error() 22 22 1
A TestMain.test_add_link_metadata() 0 26 1
A TestMain.test_notify_switch_disabled() 0 7 1
A TestMain.test_get_link_metadata() 0 18 1
A TestMain.test_delete_link_metadata() 0 36 1
A TestMain.test_add_interface_metadata_wrong_format() 0 9 1
A TestMain.test_handle_link_down_not_active() 0 16 1
A TestMain.test_delete_switch() 0 48 3
A TestMain.test_interface_link_down() 0 14 1
A TestMain.test_handle_connection_lost() 0 9 1
A TestMain.test_notify_metadata_changes() 0 13 3
A TestMain.test_handle_link_liveness_disabled() 0 19 1
A TestMain.test_handle_interface_created() 0 11 1
A TestMain.test_get_flow_id_by_intf() 0 52 1
A TestMain.test_get_flows_by_switch() 0 15 2
A TestMain.test_get_tag_ranges_by_intf() 26 26 1
A TestMain.test_handle_lldp_status_updated() 0 20 1
A TestMain.test_load_topology_does_nothing() 0 11 1
B TestMain.test_load_link() 0 61 2
A TestMain.test_fail_load_link() 0 41 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like build.tests.unit.test_main often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""Module to test the main napp file."""
2
# pylint: disable=import-error,no-name-in-module,wrong-import-order
3
# pylint: disable=import-outside-toplevel,attribute-defined-outside-init
4 1
import asyncio
5 1
import pytest
6 1
import time
7 1
from datetime import timedelta
8 1
from unittest.mock import MagicMock, create_autospec, patch, call, Mock
9 1
import tenacity
10 1
from kytos.core.common import EntityStatus
11 1
from kytos.core.helpers import now
12 1
from kytos.core.events import KytosEvent
13 1
from kytos.core.exceptions import (KytosSetTagRangeError,
14
                                   KytosTagtypeNotSupported)
15 1
from kytos.core.interface import Interface
16 1
from kytos.core.link import Link
17 1
from kytos.core.switch import Switch
18 1
from kytos.lib.helpers import (get_interface_mock, get_link_mock,
19
                               get_controller_mock, get_switch_mock,
20
                               get_test_client)
21 1
from napps.kytos.topology.exceptions import RestoreError
22
23
24 1
@pytest.mark.parametrize("liveness_status, status",
25
                         [("up", EntityStatus.UP),
26
                          ("down", EntityStatus.DOWN)])
27 1
def test_handle_link_liveness_status(liveness_status, status) -> None:
28
    """Test handle link liveness."""
29 1
    from napps.kytos.topology.main import Main
30 1
    napp = Main(get_controller_mock())
31 1
    napp.notify_topology_update = MagicMock()
32 1
    napp.notify_link_status_change = MagicMock()
33
34 1
    link = MagicMock(id="some_id", status=status)
35 1
    napp.handle_link_liveness_status(link, liveness_status)
36
37 1
    link.extend_metadata.assert_called_with({"liveness_status":
38
                                             liveness_status})
39 1
    assert napp.notify_topology_update.call_count == 1
40 1
    assert napp.notify_link_status_change.call_count == 1
41 1
    reason = f"liveness_{liveness_status}"
42 1
    napp.notify_link_status_change.assert_called_with(link, reason=reason)
43
44
45
# pylint: disable=too-many-public-methods
46 1
class TestMain:
47
    """Test the Main class."""
48
49
    # pylint: disable=too-many-public-methods, protected-access,C0302
50
51 1
    def setup_method(self):
52
        """Execute steps before each tests."""
53 1
        patch('kytos.core.helpers.run_on_thread', lambda x: x).start()
54
        # pylint: disable=import-outside-toplevel
55 1
        from napps.kytos.topology.main import Main
56 1
        Main.get_topo_controller = MagicMock()
57 1
        controller = get_controller_mock()
58 1
        self.napp = Main(controller)
59 1
        self.api_client = get_test_client(controller, self.napp)
60 1
        self.base_endpoint = 'kytos/topology/v3'
61
62 1
    def test_get_event_listeners(self):
63
        """Verify all event listeners registered."""
64 1
        expected_events = [
65
            'kytos/core.shutdown',
66
            'kytos/core.shutdown.kytos/topology',
67
            '.*.topo_controller.upsert_switch',
68
            '.*.of_lldp.network_status.updated',
69
            '.*.interface.is.nni',
70
            '.*.connection.lost',
71
            '.*.switch.interfaces.created',
72
            '.*.topology.switch.interface.created',
73
            '.*.switch.interface.deleted',
74
            '.*.switch.interface.link_down',
75
            '.*.switch.interface.link_up',
76
            '.*.switch.(new|reconnected)',
77
            'kytos/.*.liveness.(up|down|disabled)',
78
            '.*.switch.port.created',
79
            'kytos/topology.notify_link_up_if_status',
80
            'topology.interruption.(start|end)',
81
            'kytos/core.interface_tags',
82
        ]
83 1
        actual_events = self.napp.listeners()
84 1
        assert sorted(expected_events) == sorted(actual_events)
85
86 1
    def test_get_link_or_create(self):
87
        """Test _get_link_or_create."""
88 1
        dpid_a = "00:00:00:00:00:00:00:01"
89 1
        dpid_b = "00:00:00:00:00:00:00:02"
90 1
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
91 1
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
92 1
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
93 1
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
94 1
        mock_interface_a.id = dpid_a
95 1
        mock_interface_a.link = None
96 1
        mock_interface_b.id = dpid_b
97 1
        mock_interface_b.link = None
98
99 1
        link, created = self.napp._get_link_or_create(mock_interface_a,
100
                                                      mock_interface_b)
101 1
        assert created
102 1
        assert link.endpoint_a.id == dpid_a
103 1
        assert link.endpoint_b.id == dpid_b
104
105 1
        link, created = self.napp._get_link_or_create(mock_interface_a,
106
                                                      mock_interface_b)
107 1
        assert not created
108
109 1
    @patch('napps.kytos.topology.main.log')
110 1
    def test_get_link_or_create_mismatched(self, mock_log):
111
        """Test _get_link_or_create with mismatched link."""
112 1
        dpid_a = "00:00:00:00:00:00:00:01"
113 1
        dpid_b = "00:00:00:00:00:00:00:02"
114 1
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
115 1
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
116 1
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
117 1
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
118 1
        mock_interface_c = get_interface_mock('s2-eth2', 2, mock_switch_b)
119 1
        mock_interface_a.id = dpid_a + ':1'
120 1
        mock_interface_a.link = None
121 1
        mock_interface_b.id = dpid_b + ':1'
122 1
        mock_interface_b.link = None
123 1
        mock_interface_c.id = dpid_b + ':2'
124 1
        mock_interface_c.link = None
125
126 1
        link, created = self.napp._get_link_or_create(mock_interface_a,
127
                                                      mock_interface_b)
128 1
        assert created
129 1
        assert link.endpoint_a.id == mock_interface_a.id
130 1
        assert link.endpoint_b.id == mock_interface_b.id
131
132 1
        mock_interface_a.link = link
133 1
        mock_interface_b.link = link
134
135 1
        link, created = self.napp._get_link_or_create(mock_interface_a,
136
                                                      mock_interface_c)
137 1
        assert created
138 1
        assert mock_log.warning.call_count == 1
139
140 1
    def test_get_link_from_interface(self):
141
        """Test _get_link_from_interface."""
142 1
        mock_switch_a = get_switch_mock("00:00:00:00:00:00:00:01", 0x04)
143 1
        mock_switch_b = get_switch_mock("00:00:00:00:00:00:00:02", 0x04)
144 1
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
145 1
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
146 1
        mock_interface_c = get_interface_mock('s2-eth1', 2, mock_switch_b)
147 1
        mock_link = get_link_mock(mock_interface_a, mock_interface_b)
148 1
        self.napp.links = {'0e2b5d7bc858b9f38db11b69': mock_link}
149 1
        response = self.napp._get_link_from_interface(mock_interface_a)
150 1
        assert response == mock_link
151
152 1
        response = self.napp._get_link_from_interface(mock_interface_c)
153 1
        assert not response
154
155 1
    async def test_get_topology(self):
156
        """Test get_topology."""
157 1
        dpid_a = "00:00:00:00:00:00:00:01"
158 1
        dpid_b = "00:00:00:00:00:00:00:02"
159 1
        expected = {
160
                      "topology": {
161
                        "switches": {
162
                          "00:00:00:00:00:00:00:01": {
163
                            "metadata": {
164
                              "lat": "0.0",
165
                              "lng": "-30.0"
166
                            }
167
                          },
168
                          "00:00:00:00:00:00:00:02": {
169
                            "metadata": {
170
                              "lat": "0.0",
171
                              "lng": "-30.0"
172
                            }
173
                          }
174
                        },
175
                        "links": {
176
                          "cf0f4071be4": {
177
                            "id": "cf0f4071be4"
178
                          }
179
                        }
180
                      }
181
                    }
182
183 1
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
184 1
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
185 1
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
186 1
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
187
188 1
        mock_link = get_link_mock(mock_interface_a, mock_interface_b)
189 1
        mock_link.id = 'cf0f4071be4'
190 1
        mock_switch_a.id = dpid_a
191 1
        mock_switch_a.as_dict.return_value = {'metadata': {'lat': '0.0',
192
                                              'lng': '-30.0'}}
193 1
        mock_switch_b.id = dpid_b
194 1
        mock_switch_b.as_dict.return_value = {'metadata': {'lat': '0.0',
195
                                              'lng': '-30.0'}}
196
197 1
        self.napp.controller.switches = {dpid_a: mock_switch_a,
198
                                         dpid_b: mock_switch_b}
199
200 1
        self.napp.links = {"cf0f4071be4": mock_link}
201 1
        mock_link.as_dict.return_value = {"id": "cf0f4071be4"}
202 1
        endpoint = f"{self.base_endpoint}/"
203 1
        response = await self.api_client.get(endpoint)
204 1
        assert response.status_code == 200
205 1
        assert response.json() == expected
206
207 1
    def test_load_topology(self):
208
        """Test load_topology."""
209 1
        mock_buffers_put = MagicMock()
210 1
        self.napp.controller.buffers.app.put = mock_buffers_put
211 1
        link_id = \
212
            'cf0f4071be426b3f745027f5d22bc61f8312ae86293c9b28e7e66015607a9260'
213 1
        dpid_a = '00:00:00:00:00:00:00:01'
214 1
        dpid_b = '00:00:00:00:00:00:00:02'
215 1
        topology = {
216
            "topology": {
217
                "links": {
218
                    link_id: {
219
                        "enabled": True,
220
                        "id": link_id,
221
                        "endpoint_a": {"id": f"{dpid_a}:2"},
222
                        "endpoint_b": {"id": f"{dpid_b}:2"},
223
                    }
224
                },
225
                "switches": {
226
                    dpid_a: {
227
                        "dpid": dpid_a,
228
                        "enabled": True,
229
                        "metadata": {},
230
                        "id": dpid_a,
231
                        "interfaces": {
232
                            f"{dpid_a}:2": {
233
                                "enabled": True,
234
                                "metadata": {},
235
                                "lldp": True,
236
                                "port_number": 2,
237
                                "name": "s1-eth2",
238
                            }
239
                        },
240
                    },
241
                    dpid_b: {
242
                        "dpid": dpid_b,
243
                        "enabled": True,
244
                        "metadata": {},
245
                        "id": dpid_b,
246
                        "interfaces": {
247
                            f"{dpid_b}:2": {
248
                                "enabled": True,
249
                                "metadata": {},
250
                                "lldp": True,
251
                                "port_number": 2,
252
                                "name": "s2-eth2",
253
                            }
254
                        },
255
                    },
256
                },
257
            }
258
        }
259 1
        switches_expected = [dpid_a, dpid_b]
260 1
        interfaces_expected = [f'{dpid_a}:2', f'{dpid_b}:2']
261 1
        links_expected = [link_id]
262 1
        self.napp.topo_controller.get_topology.return_value = topology
263 1
        self.napp.load_topology()
264 1
        assert switches_expected == list(self.napp.controller.switches.keys())
265 1
        interfaces = []
266 1
        for switch in self.napp.controller.switches.values():
267 1
            for iface in switch.interfaces.values():
268 1
                interfaces.append(iface.id)
269 1
        assert interfaces_expected == interfaces
270 1
        assert links_expected == list(self.napp.links.keys())
271 1
        assert mock_buffers_put.call_args[1] == {"timeout": 1}
272
273 1
    @patch('napps.kytos.topology.main.Main._load_switch')
274 1
    @patch('napps.kytos.topology.main.Main._load_link')
275 1
    def test_load_topology_does_nothing(self, *args):
276
        """Test _load_network_status doing nothing."""
277 1
        (mock_load_link, mock_load_switch) = args
278 1
        self.napp.topo_controller.get_topology.return_value = {
279
            "topology": {"switches": {}, "links": {}}
280
        }
281 1
        self.napp.topo_controller.load_topology()
282 1
        assert mock_load_link.call_count == 0
283 1
        assert mock_load_switch.call_count == 0
284
285 1
    @patch('napps.kytos.topology.main.Main._load_switch')
286 1
    @patch('napps.kytos.topology.main.log')
287 1
    def test_load_topology_fail_switch(self, *args):
288
        """Test load_topology failure in switch."""
289 1
        (mock_log, mock_load_switch) = args
290 1
        topology = {
291
            'topology': {
292
                'links': {},
293
                'switches': {
294
                    '1': {}
295
                }
296
            }
297
        }
298 1
        mock_log.error.return_value = True
299 1
        self.napp.topo_controller.get_topology.return_value = topology
300 1
        mock_load_switch.side_effect = AttributeError('xpto')
301 1
        self.napp.load_topology()
302 1
        error = 'Error loading switch: xpto'
303 1
        mock_log.error.assert_called_with(error)
304
305 1
    @patch('napps.kytos.topology.main.Main._load_link')
306 1
    @patch('napps.kytos.topology.main.log')
307 1
    def test_load_topology_fail_link(self, *args):
308
        """Test load_topology failure in link."""
309 1
        (mock_log, mock_load_link) = args
310 1
        topology = {
311
            'topology': {
312
                'switches': {},
313
                'links': {
314
                    '1': {}
315
                }
316
            }
317
        }
318 1
        mock_log.error.return_value = True
319 1
        self.napp.topo_controller.get_topology.return_value = topology
320 1
        mock_load_link.side_effect = AttributeError('xpto')
321 1
        self.napp.load_topology()
322 1
        error = 'Error loading link 1: xpto'
323 1
        mock_log.error.assert_called_with(error)
324
325 1
    @patch('napps.kytos.topology.main.Main.load_interfaces_tags_values')
326 1
    @patch('napps.kytos.topology.main.KytosEvent')
327 1
    def test_load_switch(self, *args):
328
        """Test _load_switch."""
329 1
        (mock_event, mock_load_tags) = args
330 1
        mock_buffers_put = MagicMock()
331 1
        self.napp.controller.buffers.app.put = mock_buffers_put
332 1
        dpid_a = "00:00:00:00:00:00:00:01"
333 1
        dpid_x = "00:00:00:00:00:00:00:XX"
334 1
        iface_a = f'{dpid_a}:1'
335 1
        switch_attrs = {
336
            'dpid': dpid_a,
337
            'enabled': True,
338
            'id': dpid_a,
339
            'metadata': {},
340
            'interfaces': {
341
                iface_a: {
342
                    'enabled': True,
343
                    'active': True,
344
                    'lldp': True,
345
                    'id': iface_a,
346
                    'switch': dpid_a,
347
                    'metadata': {},
348
                    'name': 's2-eth1',
349
                    'port_number': 1
350
                }
351
            }
352
        }
353 1
        self.napp._load_switch(dpid_a, switch_attrs)
354
355 1
        assert len(self.napp.controller.switches) == 1
356 1
        assert dpid_a in self.napp.controller.switches
357 1
        assert dpid_x not in self.napp.controller.switches
358 1
        switch = self.napp.controller.switches[dpid_a]
359 1
        interface_details = self.napp.topo_controller.get_interfaces_details
360 1
        interface_details.assert_called_once_with([iface_a])
361 1
        mock_load_tags.assert_called()
362
363 1
        assert switch.id == dpid_a
364 1
        assert switch.dpid == dpid_a
365 1
        assert switch.is_enabled()
366 1
        assert not switch.is_active()
367
368 1
        assert len(switch.interfaces) == 1
369 1
        assert 1 in switch.interfaces
370 1
        assert 2 not in switch.interfaces
371 1
        mock_event.assert_called()
372 1
        mock_buffers_put.assert_called()
373 1
        assert mock_buffers_put.call_args[1] == {"timeout": 1}
374
375 1
        interface = switch.interfaces[1]
376 1
        assert interface.id == iface_a
377 1
        assert interface.switch.id == dpid_a
378 1
        assert interface.port_number == 1
379 1
        assert interface.is_enabled()
380 1
        assert not interface.is_active()
381 1
        assert interface.lldp
382 1
        assert interface.uni
383 1
        assert not interface.nni
384
385 1
    def test_load_switch_attrs(self):
386
        """Test _load_switch."""
387 1
        dpid_b = "00:00:00:00:00:00:00:02"
388 1
        iface_b = f'{dpid_b}:1'
389 1
        switch_attrs = {
390
            "active": True,
391
            "connection": "127.0.0.1:43230",
392
            "data_path": "XX Human readable desc of dp",
393
            "dpid": "00:00:00:00:00:00:00:02",
394
            "enabled": False,
395
            "hardware": "Open vSwitch",
396
            "id": "00:00:00:00:00:00:00:02",
397
            "interfaces": {
398
                "00:00:00:00:00:00:00:02:1": {
399
                    "active": True,
400
                    "enabled": False,
401
                    "id": "00:00:00:00:00:00:00:02:1",
402
                    "link": "",
403
                    "lldp": False,
404
                    "mac": "de:58:c3:30:b7:b7",
405
                    "metadata": {},
406
                    "name": "s2-eth1",
407
                    "nni": False,
408
                    "port_number": 1,
409
                    "speed": 1250000000,
410
                    "switch": "00:00:00:00:00:00:00:02",
411
                    "type": "interface",
412
                    "uni": True
413
                },
414
            },
415
            "manufacturer": "Nicira, Inc.",
416
            "metadata": {},
417
            "name": "00:00:00:00:00:00:00:04",
418
            "ofp_version": "0x04",
419
            "serial": "XX serial number",
420
            "software": "2.10.7",
421
            "type": "switch"
422
        }
423
424 1
        assert len(self.napp.controller.switches) == 0
425 1
        self.napp._load_switch(dpid_b, switch_attrs)
426 1
        assert len(self.napp.controller.switches) == 1
427 1
        assert dpid_b in self.napp.controller.switches
428
429 1
        switch = self.napp.controller.switches[dpid_b]
430 1
        assert switch.id == dpid_b
431 1
        assert switch.dpid == dpid_b
432 1
        assert not switch.is_enabled()
433 1
        assert not switch.is_active()
434 1
        assert switch.description['manufacturer'] == 'Nicira, Inc.'
435 1
        assert switch.description['hardware'] == 'Open vSwitch'
436 1
        assert switch.description['software'] == '2.10.7'
437 1
        assert switch.description['serial'] == 'XX serial number'
438 1
        exp_data_path = 'XX Human readable desc of dp'
439 1
        assert switch.description['data_path'] == exp_data_path
440
441 1
        assert len(switch.interfaces) == 1
442 1
        assert 1 in switch.interfaces
443 1
        assert 2 not in switch.interfaces
444
445 1
        interface = switch.interfaces[1]
446 1
        assert interface.id == iface_b
447 1
        assert interface.switch.id == dpid_b
448 1
        assert interface.port_number == 1
449 1
        assert not interface.is_enabled()
450 1
        assert not interface.lldp
451 1
        assert interface.uni
452 1
        assert not interface.nni
453
454 1
    def test_load_interfaces_tags_values(self):
455
        """Test load_interfaces_tags_values."""
456 1
        dpid_a = "00:00:00:00:00:00:00:01"
457 1
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
458 1
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
459 1
        mock_interface_a.id = dpid_a + ':1'
460 1
        mock_switch_a.interfaces = {1: mock_interface_a}
461 1
        ava_tags = {'vlan': [[10, 4095]]}
462 1
        tag_ranges = {'vlan': [[5, 4095]]}
463 1
        special_available_tags = {'vlan': ["untagged", "any"]}
464 1
        special_tags = {'vlan': ["untagged", "any"]}
465 1
        interface_details = [{
466
            "id": mock_interface_a.id,
467
            "available_tags": ava_tags,
468
            "tag_ranges": tag_ranges,
469
            "special_available_tags": special_available_tags,
470
            "special_tags": special_tags
471
        }]
472 1
        self.napp.load_interfaces_tags_values(mock_switch_a,
473
                                              interface_details)
474 1
        set_method = mock_interface_a.set_available_tags_tag_ranges
475 1
        set_method.assert_called_once_with(
476
            ava_tags, tag_ranges,
477
            special_available_tags, special_tags
478
        )
479
480 1
    def test_handle_on_interface_tags(self):
481
        """test_handle_on_interface_tags."""
482 1
        dpid_a = "00:00:00:00:00:00:00:01"
483 1
        available_tags = {'vlan': [[200, 3000]]}
484 1
        tag_ranges = {'vlan': [[20, 20], [200, 3000]]}
485 1
        special_available_tags = {'vlan': ["untagged", "any"]}
486 1
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
487 1
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
488 1
        mock_interface_a.available_tags = available_tags
489 1
        mock_interface_a.tag_ranges = tag_ranges
490 1
        mock_interface_a.special_available_tags = special_available_tags
491 1
        mock_interface_a.special_tags = special_available_tags
492 1
        self.napp.handle_on_interface_tags(mock_interface_a)
493 1
        tp_controller = self.napp.topo_controller
494 1
        args = tp_controller.upsert_interface_details.call_args[0]
495 1
        assert args[0] == '00:00:00:00:00:00:00:01:1'
496 1
        assert args[1] == {'vlan': [[200, 3000]]}
497 1
        assert args[2] == {'vlan': [[20, 20], [200, 3000]]}
498
499 1
    def test_load_link(self):
500
        """Test _load_link."""
501 1
        dpid_a = "00:00:00:00:00:00:00:01"
502 1
        dpid_b = "00:00:00:00:00:00:00:02"
503 1
        link_id = '4d42dc08522'
504 1
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
505 1
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
506 1
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
507 1
        mock_interface_a.id = dpid_a + ':1'
508 1
        mock_interface_a.available_tags = [1, 2, 3]
509 1
        mock_interface_a.link = None
510 1
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
511 1
        mock_interface_b.id = dpid_b + ':1'
512 1
        mock_interface_b.available_tags = [1, 2, 3]
513 1
        mock_interface_b.link = None
514 1
        mock_switch_a.interfaces = {1: mock_interface_a}
515 1
        mock_switch_b.interfaces = {1: mock_interface_b}
516 1
        self.napp.controller.switches[dpid_a] = mock_switch_a
517 1
        self.napp.controller.switches[dpid_b] = mock_switch_b
518 1
        link_attrs = {
519
            'enabled': True,
520
            'id': link_id,
521
            'metadata': {},
522
            'endpoint_a': {
523
                'id': mock_interface_a.id
524
            },
525
            'endpoint_b': {
526
                'id': mock_interface_b.id
527
            }
528
        }
529
530 1
        self.napp._load_link(link_attrs)
531
532 1
        assert len(self.napp.links) == 1
533 1
        link = list(self.napp.links.values())[0]
534
535 1
        assert link.endpoint_a.id == mock_interface_a.id
536 1
        assert link.endpoint_b.id == mock_interface_b.id
537 1
        assert mock_interface_a.nni
538 1
        assert mock_interface_b.nni
539 1
        assert mock_interface_a.update_link.call_count == 1
540 1
        assert mock_interface_b.update_link.call_count == 1
541
542
        # test enable/disable
543 1
        link_id = '4d42dc08522'
544 1
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
545 1
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
546 1
        mock_link = get_link_mock(mock_interface_a, mock_interface_b)
547 1
        mock_link.id = link_id
548 1
        with patch('napps.kytos.topology.main.Main._get_link_or_create',
549
                   return_value=(mock_link, True)):
550
            # enable link
551 1
            link_attrs['enabled'] = True
552 1
            self.napp.links = {link_id: mock_link}
553 1
            self.napp._load_link(link_attrs)
554 1
            assert mock_link.enable.call_count == 1
555
            # disable link
556 1
            link_attrs['enabled'] = False
557 1
            self.napp.links = {link_id: mock_link}
558 1
            self.napp._load_link(link_attrs)
559 1
            assert mock_link.disable.call_count == 1
560
561 1
    @patch('napps.kytos.topology.main.Main._get_link_or_create')
562 1
    def test_fail_load_link(self, get_link_or_create_mock):
563
        """Test fail load_link."""
564 1
        dpid_a = '00:00:00:00:00:00:00:01'
565 1
        dpid_b = '00:00:00:00:00:00:00:02'
566 1
        link_id = '4d42dc08522'
567 1
        mock_switch_a = get_switch_mock(dpid_a)
568 1
        mock_switch_b = get_switch_mock(dpid_b)
569 1
        mock_interface_a_1 = get_interface_mock('s1-eth1', 1, mock_switch_a)
570 1
        mock_interface_b_1 = get_interface_mock('s2-eth1', 1, mock_switch_b)
571 1
        mock_link = get_link_mock(mock_interface_a_1, mock_interface_b_1)
572 1
        mock_link.id = link_id
573 1
        self.napp.links = {link_id: mock_link}
574 1
        get_link_or_create_mock.return_value = mock_link
575
576 1
        link_attrs_fail = {
577
            'enabled': True,
578
            'id': link_id,
579
            'metadata': {},
580
            'endpoint_a': {
581
                'id': f"{dpid_a}:999",
582
            },
583
            'endpoint_b': {
584
                'id': f"{dpid_b}:999",
585
            }
586
        }
587 1
        with pytest.raises(RestoreError):
588 1
            self.napp._load_link(link_attrs_fail)
589
590 1
        link_attrs_fail = {
591
            'enabled': True,
592
            'id': link_id,
593
            'endpoint_a': {
594
                'id': f"{dpid_a}:1",
595
            },
596
            'endpoint_b': {
597
                'id': f"{dpid_b}:1",
598
            }
599
        }
600 1
        with pytest.raises(RestoreError):
601 1
            self.napp._load_link(link_attrs_fail)
602
603 1
    @patch('napps.kytos.topology.main.Main.notify_switch_links_status')
604 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
605 1
    async def test_enable_switch(self, mock_notify_topo, mock_sw_l_status):
606
        """Test enable_switch."""
607 1
        dpid = "00:00:00:00:00:00:00:01"
608 1
        mock_switch = get_switch_mock(dpid)
609 1
        self.napp.controller.switches = {dpid: mock_switch}
610
611 1
        endpoint = f"{self.base_endpoint}/switches/{dpid}/enable"
612 1
        response = await self.api_client.post(endpoint)
613 1
        assert response.status_code == 201
614 1
        assert mock_switch.enable.call_count == 1
615 1
        self.napp.topo_controller.enable_switch.assert_called_once_with(dpid)
616 1
        mock_notify_topo.assert_called()
617 1
        mock_sw_l_status.assert_called()
618
619
        # fail case
620 1
        mock_switch.enable.call_count = 0
621 1
        dpid = "00:00:00:00:00:00:00:02"
622 1
        endpoint = f"{self.base_endpoint}/switches/{dpid}/enable"
623 1
        response = await self.api_client.post(endpoint)
624
        assert response.status_code == 404
625
        assert mock_switch.enable.call_count == 0
626
627 1
    @patch('napps.kytos.topology.main.Main.notify_link_enabled_state')
628 1
    @patch('napps.kytos.topology.main.Main.notify_switch_links_status')
629 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
630 1
    async def test_disable_switch(self, *args):
631
        """Test disable_switch."""
632 1
        mock_notify_topo, mock_sw_l_status, mock_noti_link = args
633 1
        dpid = "00:00:00:00:00:00:00:01"
634 1
        mock_switch = get_switch_mock(dpid)
635 1
        interface = Mock()
636 1
        interface.link.is_enabled = lambda: True
637 1
        mock_switch.interfaces = {1: interface}
638 1
        self.napp.controller.switches = {dpid: mock_switch}
639
640 1
        endpoint = f"{self.base_endpoint}/switches/{dpid}/disable"
641 1
        response = await self.api_client.post(endpoint)
642 1
        assert response.status_code == 201
643 1
        assert mock_switch.disable.call_count == 1
644 1
        assert mock_noti_link.call_count == 1
645 1
        assert mock_noti_link.call_args[0][0] == interface.link
646 1
        assert mock_noti_link.call_args[0][1] == "disabled"
647 1
        assert interface.link.disable.call_count == 1
648 1
        assert self.napp.topo_controller.bulk_disable_links.call_count == 1
649 1
        self.napp.topo_controller.disable_switch.assert_called_once_with(dpid)
650 1
        mock_notify_topo.assert_called()
651 1
        mock_sw_l_status.assert_called()
652
653
        # fail case
654 1
        mock_switch.disable.call_count = 0
655 1
        dpid = "00:00:00:00:00:00:00:02"
656 1
        endpoint = f"{self.base_endpoint}/switches/{dpid}/disable"
657 1
        response = await self.api_client.post(endpoint)
658
        assert response.status_code == 404
659
        assert mock_switch.disable.call_count == 0
660
661 1
    async def test_get_switch_metadata(self):
662
        """Test get_switch_metadata."""
663 1
        dpid = "00:00:00:00:00:00:00:01"
664 1
        mock_switch = get_switch_mock(dpid)
665 1
        mock_switch.metadata = "A"
666 1
        self.napp.controller.switches = {dpid: mock_switch}
667
668 1
        endpoint = f"{self.base_endpoint}/switches/{dpid}/metadata"
669 1
        response = await self.api_client.get(endpoint)
670 1
        assert response.status_code == 200
671 1
        assert response.json() == {"metadata": mock_switch.metadata}
672
673
        # fail case
674 1
        dpid = "00:00:00:00:00:00:00:02"
675 1
        endpoint = f"{self.base_endpoint}/switches/{dpid}/metadata"
676 1
        response = await self.api_client.get(endpoint)
677
        assert response.status_code == 404
678
679 1
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
680 1
    async def test_add_switch_metadata(
681
        self, mock_metadata_changes
682
    ):
683
        """Test add_switch_metadata."""
684 1
        self.napp.controller.loop = asyncio.get_running_loop()
685 1
        dpid = "00:00:00:00:00:00:00:01"
686 1
        mock_switch = get_switch_mock(dpid)
687 1
        self.napp.controller.switches = {dpid: mock_switch}
688 1
        payload = {"data": "A"}
689
690 1
        endpoint = f"{self.base_endpoint}/switches/{dpid}/metadata"
691 1
        response = await self.api_client.post(endpoint, json=payload)
692 1
        assert response.status_code == 201
693
694 1
        mock_metadata_changes.assert_called()
695 1
        self.napp.topo_controller.add_switch_metadata.assert_called_once_with(
696
            dpid, payload
697
        )
698
699
        # fail case
700 1
        dpid = "00:00:00:00:00:00:00:02"
701 1
        endpoint = f"{self.base_endpoint}/switches/{dpid}/metadata"
702 1
        response = await self.api_client.post(endpoint, json=payload)
703
        assert response.status_code == 404
704
705 1
    async def test_add_switch_metadata_wrong_format(self):
706
        """Test add_switch_metadata_wrong_format."""
707 1
        self.napp.controller.loop = asyncio.get_running_loop()
708 1
        dpid = "00:00:00:00:00:00:00:01"
709 1
        payload = 'A'
710
711 1
        endpoint = f"{self.base_endpoint}/switches/{dpid}/metadata"
712 1
        response = await self.api_client.post(endpoint, json=payload)
713
        assert response.status_code == 400
714
715
        payload = None
716
        response = await self.api_client.post(endpoint, json=payload)
717
        assert response.status_code == 415
718
719 1
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
720 1
    async def test_delete_switch_metadata(
721
        self, mock_metadata_changes
722
    ):
723
        """Test delete_switch_metadata."""
724 1
        self.napp.controller.loop = asyncio.get_running_loop()
725 1
        dpid = "00:00:00:00:00:00:00:01"
726 1
        mock_switch = get_switch_mock(dpid)
727 1
        mock_switch.metadata = {"A": "A"}
728 1
        self.napp.controller.switches = {dpid: mock_switch}
729
730 1
        key = "A"
731 1
        endpoint = f"{self.base_endpoint}/switches/{dpid}/metadata/{key}"
732 1
        response = await self.api_client.delete(endpoint)
733
734 1
        assert response.status_code == 200
735 1
        assert mock_metadata_changes.call_count == 1
736 1
        del_key_mock = self.napp.topo_controller.delete_switch_metadata_key
737 1
        del_key_mock.assert_called_with(
738
            dpid, key
739
        )
740
741
        # 404, Metadata not found
742 1
        mock_switch.metadata = {}
743 1
        endpoint = f"{self.base_endpoint}/switches/{dpid}/metadata/{key}"
744 1
        response = await self.api_client.delete(endpoint)
745
        assert response.status_code == 404
746
747
        # 404, Switch not found
748
        key = "A"
749
        dpid = "00:00:00:00:00:00:00:02"
750
        endpoint = f"{self.base_endpoint}/switches/{dpid}/metadata/{key}"
751
        response = await self.api_client.delete(endpoint)
752
        assert mock_metadata_changes.call_count == 1
753
        assert response.status_code == 404
754
755
    # pylint: disable=too-many-statements
756 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
757 1
    async def test_enable_interfaces(self, mock_notify_topo):
758
        """Test enable_interfaces."""
759 1
        dpid = '00:00:00:00:00:00:00:01'
760 1
        mock_switch = get_switch_mock(dpid)
761 1
        mock_interface_1 = get_interface_mock('s1-eth1', 1, mock_switch)
762 1
        mock_interface_1.link = Mock()
763 1
        mock_interface_1.link._enabled = True
764 1
        mock_interface_2 = get_interface_mock('s1-eth2', 2, mock_switch)
765 1
        mock_interface_2.link = Mock()
766 1
        mock_interface_2.link._enabled = False
767 1
        mock_switch.interfaces = {1: mock_interface_1, 2: mock_interface_2}
768
769
        # Switch not found
770 1
        interface_id = '00:00:00:00:00:00:00:01:1'
771 1
        endpoint = f"{self.base_endpoint}/interfaces/{interface_id}/enable"
772 1
        response = await self.api_client.post(endpoint)
773
        assert response.status_code == 404
774
775
        # Switch not enabled
776 1
        mock_switch.is_enabled = lambda: False
777
        self.napp.controller.switches = {dpid: mock_switch}
778
        endpoint = f"{self.base_endpoint}/interfaces/{interface_id}/enable"
779
        response = await self.api_client.post(endpoint)
780
        assert response.status_code == 409
781
782
        # Success
783 1
        mock_switch.is_enabled = lambda: True
784
        endpoint = f"{self.base_endpoint}/interfaces/{interface_id}/enable"
785
        response = await self.api_client.post(endpoint)
786 1
        assert response.status_code == 200
787 1
        assert mock_interface_1.enable.call_count == 1
788 1
        assert mock_interface_2.enable.call_count == 0
789 1
        self.napp.topo_controller.enable_interface.assert_called_with(
790
            interface_id
791
        )
792 1
        mock_notify_topo.assert_called()
793
794 1
        mock_interface_1.enable.call_count = 0
795 1
        mock_interface_2.enable.call_count = 0
796 1
        endpoint = f"{self.base_endpoint}/interfaces/switch/{dpid}/enable"
797 1
        response = await self.api_client.post(endpoint)
798 1
        assert response.status_code == 200
799 1
        self.napp.topo_controller.upsert_switch.assert_called_with(
800
            mock_switch.id, mock_switch.as_dict()
801
        )
802 1
        assert mock_interface_1.enable.call_count == 1
803 1
        assert mock_interface_2.enable.call_count == 1
804
805
        # test interface not found
806 1
        interface_id = '00:00:00:00:00:00:00:01:3'
807 1
        mock_interface_1.enable.call_count = 0
808 1
        mock_interface_2.enable.call_count = 0
809 1
        endpoint = f"{self.base_endpoint}/interfaces/{interface_id}/enable"
810 1
        response = await self.api_client.post(endpoint)
811
        assert response.status_code == 404
812
        assert mock_interface_1.enable.call_count == 0
813
        assert mock_interface_2.enable.call_count == 0
814
815
        # test switch not found
816
        dpid = '00:00:00:00:00:00:00:02'
817
        endpoint = f"{self.base_endpoint}/interfaces/{interface_id}/enable"
818
        response = await self.api_client.post(endpoint)
819
        assert response.status_code == 404
820
        assert mock_interface_1.enable.call_count == 0
821
        assert mock_interface_2.enable.call_count == 0
822
823 1
    @patch('napps.kytos.topology.main.Main.notify_link_enabled_state')
824 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
825 1
    async def test_disable_interfaces(self, mock_notify_topo, mock_noti_link):
826
        """Test disable_interfaces."""
827 1
        interface_id = '00:00:00:00:00:00:00:01:1'
828 1
        dpid = '00:00:00:00:00:00:00:01'
829 1
        mock_switch = get_switch_mock(dpid)
830 1
        mock_interface_1 = get_interface_mock('s1-eth1', 1, mock_switch)
831 1
        mock_interface_1.link = Mock()
832 1
        mock_interface_1.link.is_enabled = lambda: True
833 1
        mock_interface_2 = get_interface_mock('s1-eth2', 2, mock_switch)
834 1
        mock_interface_2.link = Mock()
835 1
        mock_interface_2.link.is_enabled = lambda: False
836 1
        mock_switch.interfaces = {1: mock_interface_1, 2: mock_interface_2}
837 1
        self.napp.controller.switches = {dpid: mock_switch}
838
839 1
        endpoint = f"{self.base_endpoint}/interfaces/{interface_id}/disable"
840 1
        response = await self.api_client.post(endpoint)
841 1
        assert response.status_code == 200
842
843 1
        self.napp.topo_controller.disable_interface.assert_called_with(
844
            interface_id
845
        )
846 1
        assert mock_interface_1.disable.call_count == 1
847 1
        assert mock_interface_2.disable.call_count == 0
848 1
        assert mock_interface_1.link.disable.call_count == 1
849 1
        assert mock_noti_link.call_count == 1
850 1
        assert mock_noti_link.call_args[0][0] == mock_interface_1.link
851 1
        assert mock_noti_link.call_args[0][1] == "disabled"
852 1
        assert self.napp.topo_controller.disable_interface.call_count == 1
853 1
        mock_notify_topo.assert_called()
854
855 1
        mock_interface_1.disable.call_count = 0
856 1
        mock_interface_2.disable.call_count = 0
857
858 1
        endpoint = f"{self.base_endpoint}/interfaces/switch/{dpid}/disable"
859 1
        response = await self.api_client.post(endpoint)
860 1
        assert response.status_code == 200
861
862 1
        self.napp.topo_controller.upsert_switch.assert_called_with(
863
            mock_switch.id, mock_switch.as_dict()
864
        )
865 1
        assert mock_interface_1.disable.call_count == 1
866 1
        assert mock_interface_1.link.disable.call_count == 2
867 1
        assert mock_interface_2.disable.call_count == 1
868 1
        assert mock_noti_link.call_count == 2
869 1
        assert mock_noti_link.call_args[0][0] == mock_interface_1.link
870 1
        assert mock_noti_link.call_args[0][1] == "disabled"
871 1
        bulk_controller = self.napp.topo_controller.bulk_disable_links
872 1
        assert bulk_controller.call_count == 1
873 1
        assert len(bulk_controller.call_args[0][0]) == 1
874
875
        # test interface not found
876 1
        interface_id = '00:00:00:00:00:00:00:01:3'
877 1
        mock_interface_1.disable.call_count = 0
878 1
        mock_interface_2.disable.call_count = 0
879 1
        endpoint = f"{self.base_endpoint}/interfaces/{interface_id}/disable"
880 1
        response = await self.api_client.post(endpoint)
881
882
        assert response.status_code == 404
883
        assert mock_interface_1.disable.call_count == 0
884
        assert mock_interface_2.disable.call_count == 0
885
886
        # test switch not found
887
        dpid = '00:00:00:00:00:00:00:02'
888
        endpoint = f"{self.base_endpoint}/interfaces/switch/{dpid}/disable"
889
        response = await self.api_client.post(endpoint)
890
        assert response.status_code == 404
891
        assert mock_interface_1.disable.call_count == 0
892
        assert mock_interface_2.disable.call_count == 0
893
894 1
    async def test_get_interface_metadata(self):
895
        """Test get_interface_metada."""
896 1
        interface_id = '00:00:00:00:00:00:00:01:1'
897 1
        dpid = '00:00:00:00:00:00:00:01'
898 1
        mock_switch = get_switch_mock(dpid)
899 1
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
900 1
        mock_interface.metadata = {"A": "B"}
901 1
        mock_switch.interfaces = {1: mock_interface}
902 1
        self.napp.controller.switches = {dpid: mock_switch}
903
904 1
        endpoint = f"{self.base_endpoint}/interfaces/{interface_id}/metadata"
905 1
        response = await self.api_client.get(endpoint)
906 1
        assert response.status_code == 200
907 1
        assert response.json() == {"metadata": mock_interface.metadata}
908
909
        # fail case switch not found
910 1
        interface_id = '00:00:00:00:00:00:00:02:1'
911 1
        endpoint = f"{self.base_endpoint}/interfaces/{interface_id}/metadata"
912 1
        response = await self.api_client.get(endpoint)
913
        assert response.status_code == 404
914
915
        # fail case interface not found
916
        interface_id = '00:00:00:00:00:00:00:01:2'
917
        endpoint = f"{self.base_endpoint}/interfaces/{interface_id}/metadata"
918
        response = await self.api_client.get(endpoint)
919
        assert response.status_code == 404
920
921 1
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
922 1
    async def test_add_interface_metadata(
923
        self, mock_metadata_changes
924
    ):
925
        """Test add_interface_metadata."""
926 1
        self.napp.controller.loop = asyncio.get_running_loop()
927 1
        interface_id = '00:00:00:00:00:00:00:01:1'
928 1
        dpid = '00:00:00:00:00:00:00:01'
929 1
        mock_switch = get_switch_mock(dpid)
930 1
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
931 1
        mock_interface.metadata = {"metada": "A"}
932 1
        mock_switch.interfaces = {1: mock_interface}
933 1
        self.napp.controller.switches = {dpid: mock_switch}
934 1
        payload = {"metada": "A"}
935 1
        endpoint = f"{self.base_endpoint}/interfaces/{interface_id}/metadata"
936 1
        response = await self.api_client.post(endpoint, json=payload)
937 1
        assert response.status_code == 201
938 1
        mock_metadata_changes.assert_called()
939
940
        # fail case switch not found
941 1
        interface_id = '00:00:00:00:00:00:00:02:1'
942 1
        endpoint = f"{self.base_endpoint}/interfaces/{interface_id}/metadata"
943 1
        response = await self.api_client.post(endpoint, json=payload)
944
        assert response.status_code == 404
945
946
        # fail case interface not found
947
        interface_id = '00:00:00:00:00:00:00:01:2'
948
        endpoint = f"{self.base_endpoint}/interfaces/{interface_id}/metadata"
949
        response = await self.api_client.post(endpoint, json=payload)
950
        assert response.status_code == 404
951
952 1
    async def test_add_interface_metadata_wrong_format(self):
953
        """Test add_interface_metadata_wrong_format."""
954 1
        self.napp.controller.loop = asyncio.get_running_loop()
955 1
        interface_id = "00:00:00:00:00:00:00:01:1"
956 1
        endpoint = f"{self.base_endpoint}/interfaces/{interface_id}/metadata"
957 1
        response = await self.api_client.post(endpoint, json='A')
958
        assert response.status_code == 400
959
        response = await self.api_client.post(endpoint, json=None)
960
        assert response.status_code == 415
961
962 1
    async def test_delete_interface_metadata(self):
963
        """Test delete_interface_metadata."""
964 1
        self.napp.controller.loop = asyncio.get_running_loop()
965 1
        interface_id = '00:00:00:00:00:00:00:01:1'
966 1
        dpid = '00:00:00:00:00:00:00:01'
967 1
        mock_switch = get_switch_mock(dpid)
968 1
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
969 1
        mock_interface.remove_metadata.side_effect = [True, False]
970 1
        mock_interface.metadata = {"A": "A"}
971 1
        mock_switch.interfaces = {1: mock_interface}
972 1
        self.napp.controller.switches = {'00:00:00:00:00:00:00:01':
973
                                         mock_switch}
974
975 1
        key = 'A'
976 1
        url = f"{self.base_endpoint}/interfaces/{interface_id}/metadata/{key}"
977 1
        response = await self.api_client.delete(url)
978 1
        assert response.status_code == 200
979
980 1
        del_key_mock = self.napp.topo_controller.delete_interface_metadata_key
981 1
        del_key_mock.assert_called_once_with(interface_id, key)
982
983
        # fail case switch not found
984 1
        key = 'A'
985 1
        interface_id = '00:00:00:00:00:00:00:02:1'
986 1
        url = f"{self.base_endpoint}/interfaces/{interface_id}/metadata/{key}"
987 1
        response = await self.api_client.delete(url)
988
        assert response.status_code == 404
989
990
        # fail case interface not found
991
        key = 'A'
992
        interface_id = '00:00:00:00:00:00:00:01:2'
993
        url = f"{self.base_endpoint}/interfaces/{interface_id}/metadata/{key}"
994
        response = await self.api_client.delete(url)
995
        assert response.status_code == 404
996
997
        # fail case metadata not found
998
        key = 'B'
999
        interface_id = '00:00:00:00:00:00:00:01:1'
1000
        url = f"{self.base_endpoint}/interfaces/{interface_id}/metadata/{key}"
1001
        response = await self.api_client.delete(url)
1002
        assert response.status_code == 404
1003
1004 1
    @patch('napps.kytos.topology.main.Main.notify_link_enabled_state')
1005 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1006 1
    async def test_enable_link(self, mock_notify_topo, mock_noti_link):
1007
        """Test enable_link."""
1008 1
        mock_link = MagicMock(Link)
1009 1
        link_id = "1"
1010 1
        mock_link.id = link_id
1011 1
        mock_link.is_enabled = lambda: False
1012 1
        mock_link.endpoint_a = MagicMock(is_enabled=lambda: True)
1013 1
        mock_link.endpoint_b = MagicMock(is_enabled=lambda: True)
1014 1
        self.napp.links = {'1': mock_link}
1015
1016
        # 409, endpoint is/are disabled
1017 1
        mock_link.endpoint_a.is_enabled = lambda: False
1018 1
        mock_link.endpoint_b.is_enabled = lambda: False
1019 1
        endpoint = f"{self.base_endpoint}/links/{link_id}/enable"
1020 1
        response = await self.api_client.post(endpoint)
1021
        assert response.status_code == 409
1022
1023 1
        mock_link.endpoint_a.is_enabled = lambda: True
1024
        endpoint = f"{self.base_endpoint}/links/{link_id}/enable"
1025
        response = await self.api_client.post(endpoint)
1026
        assert response.status_code == 409
1027
1028
        # Success
1029 1
        mock_link.endpoint_b.is_enabled = lambda: True
1030
        endpoint = f"{self.base_endpoint}/links/{link_id}/enable"
1031
        response = await self.api_client.post(endpoint)
1032 1
        assert response.status_code == 201
1033 1
        assert mock_noti_link.call_count == 1
1034 1
        assert mock_noti_link.call_args[0][0] == mock_link
1035 1
        assert mock_noti_link.call_args[0][1] == "enabled"
1036 1
        mock_notify_topo.assert_called()
1037
1038
        # 404, link not found
1039 1
        link_id = "2"
1040 1
        endpoint = f"{self.base_endpoint}/links/{link_id}/enable"
1041 1
        response = await self.api_client.post(endpoint)
1042
        assert response.status_code == 404
1043
        assert mock_noti_link.call_count == 1
1044
1045 1
    @patch('napps.kytos.topology.main.Main.notify_link_enabled_state')
1046 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1047 1
    async def test_disable_link(self, mock_notify_topo, mock_notify):
1048
        """Test disable_link."""
1049 1
        mock_link = MagicMock(Link)
1050 1
        mock_link.is_enabled = lambda: True
1051 1
        self.napp.links = {'1': mock_link}
1052
1053 1
        link_id = "1"
1054 1
        endpoint = f"{self.base_endpoint}/links/{link_id}/disable"
1055 1
        response = await self.api_client.post(endpoint)
1056 1
        assert response.status_code == 201
1057 1
        assert mock_notify_topo.call_count == 1
1058 1
        assert mock_notify.call_count == 1
1059 1
        assert mock_notify.call_args[0][0] == mock_link
1060 1
        assert mock_notify.call_args[0][1] == "disabled"
1061 1
        assert mock_link.disable.call_count == 1
1062 1
        assert self.napp.topo_controller.disable_link.call_count == 1
1063
1064
        # fail case
1065 1
        link_id = "2"
1066 1
        endpoint = f"{self.base_endpoint}/links/{link_id}/disable"
1067 1
        response = await self.api_client.post(endpoint)
1068
        assert response.status_code == 404
1069
1070 1
    def test_handle_lldp_status_updated(self):
1071
        """Test handle_lldp_status_updated."""
1072 1
        event = MagicMock()
1073 1
        self.napp.controller.buffers.app.put = MagicMock()
1074
1075 1
        dpid_a = "00:00:00:00:00:00:00:01"
1076 1
        dpid_b = "00:00:00:00:00:00:00:02"
1077 1
        dpids = [dpid_a, dpid_b]
1078 1
        interface_ids = [f"{dpid}:1" for dpid in dpids]
1079
1080 1
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
1081 1
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
1082 1
        self.napp.controller.switches = {dpid_a: mock_switch_a,
1083
                                         dpid_b: mock_switch_b}
1084
1085 1
        event.content = {"interface_ids": interface_ids, "state": "disabled"}
1086 1
        self.napp.handle_lldp_status_updated(event)
1087
1088 1
        mock_put = self.napp.controller.buffers.app.put
1089 1
        assert mock_put.call_count == len(interface_ids)
1090
1091 1
    def test_handle_topo_controller_upsert_switch(self):
1092
        """Test handle_topo_controller_upsert_switch."""
1093 1
        event = MagicMock()
1094 1
        self.napp.handle_topo_controller_upsert_switch(event)
1095 1
        mock = self.napp.topo_controller.upsert_switch
1096 1
        mock.assert_called_with(event.id, event.as_dict())
1097
1098 1
    async def test_get_link_metadata(self):
1099
        """Test get_link_metadata."""
1100 1
        mock_link = MagicMock(Link)
1101 1
        mock_link.metadata = "A"
1102 1
        self.napp.links = {'1': mock_link}
1103 1
        msg_success = {"metadata": "A"}
1104
1105 1
        link_id = "1"
1106 1
        endpoint = f"{self.base_endpoint}/links/{link_id}/metadata"
1107 1
        response = await self.api_client.get(endpoint)
1108 1
        assert response.status_code == 200
1109 1
        assert msg_success == response.json()
1110
1111
        # fail case
1112 1
        link_id = "2"
1113 1
        endpoint = f"{self.base_endpoint}/links/{link_id}/metadata"
1114 1
        response = await self.api_client.get(endpoint)
1115
        assert response.status_code == 404
1116
1117 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1118 1
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
1119 1
    async def test_add_link_metadata(
1120
        self,
1121
        mock_metadata_changes,
1122
        mock_topology_update
1123
    ):
1124
        """Test add_link_metadata."""
1125 1
        self.napp.controller.loop = asyncio.get_running_loop()
1126 1
        mock_link = MagicMock(Link)
1127 1
        mock_link.metadata = "A"
1128 1
        self.napp.links = {'1': mock_link}
1129 1
        payload = {"metadata": "A"}
1130 1
        link_id = 1
1131
1132 1
        endpoint = f"{self.base_endpoint}/links/{link_id}/metadata"
1133 1
        response = await self.api_client.post(endpoint, json=payload)
1134 1
        assert response.status_code == 201
1135 1
        mock_metadata_changes.assert_called()
1136 1
        mock_topology_update.assert_called()
1137
1138
        # fail case
1139 1
        link_id = 2
1140 1
        endpoint = f"{self.base_endpoint}/links/{link_id}/metadata"
1141 1
        response = await self.api_client.post(endpoint, json=payload)
1142
        assert response.status_code == 404
1143
1144 1
    async def test_add_link_metadata_wrong_format(self):
1145
        """Test add_link_metadata_wrong_format."""
1146 1
        self.napp.controller.loop = asyncio.get_running_loop()
1147 1
        link_id = 'cf0f4071be426b3f745027f5d22'
1148 1
        payload = "A"
1149 1
        endpoint = f"{self.base_endpoint}/links/{link_id}/metadata"
1150 1
        response = await self.api_client.post(endpoint, json=payload)
1151
        assert response.status_code == 400
1152
1153
        payload = None
1154
        response = await self.api_client.post(endpoint, json=payload)
1155
        assert response.status_code == 415
1156
1157 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1158 1
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
1159 1
    async def test_delete_link_metadata(
1160
        self,
1161
        mock_metadata_changes,
1162
        mock_topology_update
1163
    ):
1164
        """Test delete_link_metadata."""
1165 1
        mock_link = MagicMock(Link)
1166 1
        mock_link.metadata = {"A": "A"}
1167 1
        mock_link.remove_metadata.side_effect = [True, False]
1168 1
        self.napp.links = {'1': mock_link}
1169
1170 1
        link_id = 1
1171 1
        key = 'A'
1172 1
        endpoint = f"{self.base_endpoint}/links/{link_id}/metadata/{key}"
1173 1
        response = await self.api_client.delete(endpoint)
1174 1
        assert response.status_code == 200
1175 1
        del_mock = self.napp.topo_controller.delete_link_metadata_key
1176 1
        del_mock.assert_called_once_with(mock_link.id, key)
1177 1
        mock_metadata_changes.assert_called()
1178 1
        mock_topology_update.assert_called()
1179
1180
        # fail case link not found
1181 1
        link_id = 2
1182 1
        key = 'A'
1183 1
        endpoint = f"{self.base_endpoint}/links/{link_id}/metadata/{key}"
1184 1
        response = await self.api_client.delete(endpoint)
1185
        assert response.status_code == 404
1186
1187
        # fail case metadata not found
1188
        link_id = 1
1189
        key = 'B'
1190
        endpoint = f"{self.base_endpoint}/links/{link_id}/metadata/{key}"
1191
        response = await self.api_client.delete(endpoint)
1192
        assert response.status_code == 404
1193
1194 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1195 1
    def test_handle_new_switch(self, mock_notify_topology_update):
1196
        """Test handle_new_switch."""
1197 1
        mock_event = MagicMock()
1198 1
        mock_switch = create_autospec(Switch)
1199 1
        mock_event.content['switch'] = mock_switch
1200 1
        self.napp.handle_new_switch(mock_event)
1201 1
        mock = self.napp.topo_controller.upsert_switch
1202 1
        mock.assert_called_once_with(mock_event.content['switch'].id,
1203
                                     mock_event.content['switch'].as_dict())
1204 1
        mock_notify_topology_update.assert_called()
1205
1206 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1207 1
    def test_handle_connection_lost(self, mock_notify_topology_update):
1208
        """Test handle connection_lost."""
1209 1
        mock_event = MagicMock()
1210 1
        mock_switch = create_autospec(Switch)
1211 1
        mock_switch.return_value = True
1212 1
        mock_event.content['source'] = mock_switch
1213 1
        self.napp.handle_connection_lost(mock_event)
1214 1
        mock_notify_topology_update.assert_called()
1215
1216 1
    @patch('napps.kytos.topology.main.Main.handle_interface_link_down')
1217 1
    @patch('napps.kytos.topology.main.Main.handle_interface_link_up')
1218 1
    def test_handle_interface_created(self, mock_link_up, mock_link_down):
1219
        """Test handle_interface_created."""
1220 1
        mock_event = MagicMock()
1221 1
        mock_interface = create_autospec(Interface)
1222 1
        mock_interface.id = "1"
1223 1
        mock_event.content = {'interface': mock_interface}
1224 1
        self.napp.handle_interface_created(mock_event)
1225 1
        mock_link_up.assert_called()
1226 1
        mock_link_down.assert_not_called()
1227
1228 1
    @patch('napps.kytos.topology.main.Main.handle_interface_link_down')
1229 1
    @patch('napps.kytos.topology.main.Main.handle_interface_link_up')
1230 1
    def test_handle_interface_created_inactive(self, mock_link_up,
1231
                                               mock_link_down):
1232
        """Test handle_interface_created inactive."""
1233 1
        mock_event = MagicMock()
1234 1
        mock_interface = create_autospec(Interface)
1235 1
        mock_interface.id = "1"
1236 1
        mock_event.content = {'interface': mock_interface}
1237 1
        mock_interface.is_active.return_value = False
1238 1
        self.napp.handle_interface_created(mock_event)
1239 1
        mock_link_up.assert_not_called()
1240 1
        mock_link_down.assert_called()
1241
1242 1
    def test_handle_interfaces_created(self):
1243
        """Test handle_interfaces_created."""
1244 1
        buffers_app_mock = MagicMock()
1245 1
        self.napp.controller.buffers.app = buffers_app_mock
1246 1
        mock_switch = create_autospec(Switch)
1247 1
        mock_event = MagicMock()
1248 1
        mock_interface = create_autospec(Interface)
1249 1
        mock_interface.id = "1"
1250 1
        mock_interface.switch = mock_switch
1251 1
        mock_interface_two = create_autospec(Interface)
1252 1
        mock_interface_two.id = "2"
1253 1
        mock_event.content = {'interfaces': [mock_interface,
1254
                              mock_interface_two]}
1255 1
        self.napp.handle_interfaces_created(mock_event)
1256 1
        upsert_mock = self.napp.topo_controller.upsert_switch
1257 1
        upsert_mock.assert_called_with(mock_switch.id, mock_switch.as_dict())
1258 1
        assert self.napp.controller.buffers.app.put.call_count == 2
1259
1260 1
    @patch('napps.kytos.topology.main.Main.handle_interface_link_down')
1261 1
    def test_handle_interface_down(self, mock_handle_interface_link_down):
1262
        """Test handle interface down."""
1263 1
        mock_event = MagicMock()
1264 1
        mock_interface = create_autospec(Interface)
1265 1
        mock_event.content['interface'] = mock_interface
1266 1
        self.napp.handle_interface_down(mock_event)
1267 1
        mock_handle_interface_link_down.assert_called()
1268
1269 1
    @patch('napps.kytos.topology.main.Main.handle_interface_down')
1270 1
    def test_interface_deleted(self, mock_handle_interface_link_down):
1271
        """Test interface deleted."""
1272 1
        mock_event = MagicMock()
1273 1
        self.napp.handle_interface_deleted(mock_event)
1274 1
        mock_handle_interface_link_down.assert_called()
1275
1276 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1277 1
    def test_interface_link_up(self, mock_notify_topology_update):
1278
        """Test interface link_up."""
1279 1
        self.napp.controller.buffers.app.put = MagicMock()
1280
1281 1
        tnow = time.time()
1282 1
        mock_switch_a = create_autospec(Switch)
1283 1
        mock_switch_a.is_active.return_value = True
1284 1
        mock_switch_b = create_autospec(Switch)
1285 1
        mock_switch_b.is_active.return_value = True
1286 1
        mock_interface_a = create_autospec(Interface)
1287 1
        mock_interface_a.switch = mock_switch_a
1288 1
        mock_interface_a.is_active.return_value = False
1289 1
        mock_interface_b = create_autospec(Interface)
1290 1
        mock_interface_b.switch = mock_switch_b
1291 1
        mock_interface_b.is_active.return_value = True
1292 1
        mock_link = create_autospec(Link)
1293 1
        mock_link.get_metadata.return_value = tnow
1294 1
        mock_link.is_active.return_value = False
1295 1
        mock_link.endpoint_a = mock_interface_a
1296 1
        mock_link.endpoint_b = mock_interface_b
1297 1
        mock_link.status = EntityStatus.UP
1298 1
        mock_interface_a.link = mock_link
1299 1
        mock_interface_b.link = mock_link
1300 1
        event = KytosEvent("kytos.of_core.switch.interface.down")
1301 1
        self.napp.handle_interface_link_up(mock_interface_a, event)
1302 1
        mock_notify_topology_update.assert_called()
1303 1
        assert mock_link.id in self.napp.link_status_change
1304 1
        mock_link.activate.assert_called()
1305 1
        self.napp.controller.buffers.app.put.assert_not_called()
1306
1307 1
        mock_interface_a.is_active.return_value = True
1308 1
        event = KytosEvent("kytos.of_core.switch.interface.down")
1309 1
        self.napp.handle_interface_link_up(mock_interface_a, event)
1310
1311 1
        assert mock_link.id in self.napp.link_status_change
1312 1
        link_status_info = self.napp.link_status_change[mock_link.id]
1313 1
        mock_link.activate.assert_called()
1314 1
        assert self.napp.controller.buffers.app.put.call_count == 1
1315 1
        ev = "kytos/topology.notify_link_up_if_status"
1316 1
        assert self.napp.controller.buffers.app.put.call_args[0][0].name == ev
1317
1318 1
        mock_link.is_active.return_value = True
1319 1
        orig_change_time = link_status_info["last_status_change"]
1320
1321 1
        self.napp.handle_interface_link_up(mock_interface_a, event)
1322
1323 1
        link_status_info = self.napp.link_status_change[mock_link.id]
1324 1
        new_change_time = link_status_info["last_status_change"]
1325 1
        assert orig_change_time == new_change_time
1326
1327 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1328 1
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1329 1
    def test_interface_link_down(self, *args):
1330
        """Test interface link down."""
1331 1
        mock_status_change, mock_topology_update = args
1332
1333 1
        mock_interface = create_autospec(Interface)
1334 1
        mock_link = create_autospec(Link)
1335 1
        mock_link.is_active.return_value = True
1336 1
        mock_interface.link = mock_link
1337 1
        event = KytosEvent("kytos.of_core.switch.interface.link_up")
1338 1
        self.napp.handle_interface_link_down(mock_interface, event)
1339 1
        mock_topology_update.assert_called()
1340 1
        mock_status_change.assert_called()
1341
1342 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1343 1
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1344 1
    def test_interface_link_down_unordered_event(self, *args):
1345
        """Test interface link down unordered event."""
1346 1
        (mock_status_change, mock_topology_update) = args
1347
1348 1
        mock_interface = create_autospec(Interface)
1349 1
        mock_interface.id = "1"
1350 1
        event_2 = KytosEvent("kytos.of_core.switch.interface.down")
1351 1
        event_1 = KytosEvent("kytos.of_core.switch.interface.up")
1352 1
        assert event_1.timestamp > event_2.timestamp
1353 1
        self.napp._intfs_updated_at[mock_interface.id] = event_1.timestamp
1354 1
        self.napp.handle_interface_link_down(mock_interface, event_2)
1355 1
        mock_topology_update.assert_not_called()
1356 1
        mock_status_change.assert_not_called()
1357
1358 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1359 1
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1360 1
    def test_interface_link_up_unordered_event(self, *args):
1361
        """Test interface link up unordered event."""
1362 1
        (mock_status_change, mock_topology_update) = args
1363
1364 1
        mock_interface = create_autospec(Interface)
1365 1
        mock_interface.id = "1"
1366 1
        event_2 = KytosEvent("kytos.of_core.switch.interface.up")
1367 1
        event_1 = KytosEvent("kytos.of_core.switch.interface.down")
1368 1
        assert event_1.timestamp > event_2.timestamp
1369 1
        self.napp._intfs_updated_at[mock_interface.id] = event_1.timestamp
1370 1
        self.napp.handle_interface_link_up(mock_interface, event_2)
1371 1
        mock_topology_update.assert_not_called()
1372 1
        mock_status_change.assert_not_called()
1373
1374 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1375 1
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1376 1
    def test_handle_link_down(self, *args):
1377
        """Test interface link down."""
1378 1
        (mock_status_change, mock_topology_update) = args
1379
1380 1
        mock_interface = create_autospec(Interface)
1381 1
        mock_link = create_autospec(Link)
1382 1
        mock_link.is_active.return_value = True
1383 1
        mock_interface.link = mock_link
1384 1
        self.napp.handle_link_down(mock_interface)
1385 1
        mock_interface.deactivate.assert_not_called()
1386 1
        mock_link.deactivate.assert_called()
1387 1
        assert mock_topology_update.call_count == 1
1388 1
        mock_status_change.assert_called()
1389
1390 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1391 1
    def test_handle_link_down_not_active(self, args):
1392
        """Test interface link down with link not active."""
1393 1
        mock_topology_update = args
1394 1
        self.napp.controller.buffers.app.put = MagicMock()
1395
1396 1
        mock_interface = create_autospec(Interface)
1397 1
        mock_link = create_autospec(Link)
1398 1
        mock_link.is_active.return_value = False
1399 1
        mock_link.get_metadata.return_value = False
1400 1
        mock_interface.link = mock_link
1401 1
        self.napp.link_up = set()
1402 1
        self.napp.link_status_change[mock_link.id] = {}
1403 1
        self.napp.handle_link_down(mock_interface)
1404 1
        mock_topology_update.assert_called()
1405 1
        self.napp.controller.buffers.app.put.assert_not_called()
1406
1407 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1408 1
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1409 1
    def test_handle_link_down_not_active_last_status(self, *args):
1410
        """Test interface link down with link not active."""
1411 1
        (mock_status_change, mock_topology_update) = args
1412
1413 1
        mock_interface = create_autospec(Interface)
1414 1
        mock_link = create_autospec(Link)
1415 1
        mock_link.is_active.return_value = False
1416 1
        mock_link.get_metadata.return_value = True
1417 1
        mock_interface.link = mock_link
1418 1
        self.napp.handle_link_down(mock_interface)
1419 1
        mock_topology_update.assert_called()
1420 1
        mock_status_change.assert_called()
1421
1422 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1423 1
    def test_handle_link_up(self, mock_notify_topology_update):
1424
        """Test handle link up."""
1425 1
        mock_switch_a = create_autospec(Switch)
1426 1
        mock_switch_a.is_active.return_value = True
1427 1
        mock_interface = create_autospec(Interface)
1428 1
        mock_interface.switch = mock_switch_a
1429 1
        mock_interface.is_active.return_value = True
1430 1
        mock_link = MagicMock(status=EntityStatus.UP)
1431 1
        mock_link.is_active.return_value = True
1432 1
        mock_interface.link = mock_link
1433 1
        self.napp.handle_link_up(mock_interface)
1434 1
        mock_interface.activate.assert_not_called()
1435 1
        mock_notify_topology_update.assert_called()
1436 1
        assert self.napp.controller.buffers.app.put.call_count == 2
1437 1
        ev = "kytos/topology.notify_link_up_if_status"
1438 1
        assert self.napp.controller.buffers.app.put.call_args[0][0].name == ev
1439
1440 1
    @patch('time.sleep')
1441 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1442 1
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1443 1
    def test_handle_link_up_intf_down(self, *args):
1444
        """Test handle link up but one intf down."""
1445 1
        (mock_status_change, mock_topology_update, _) = args
1446
1447 1
        mock_switch = create_autospec(Switch)
1448 1
        mock_interface = create_autospec(Interface)
1449 1
        mock_interface.switch = mock_switch
1450 1
        mock_link = MagicMock()
1451 1
        mock_link.endpoint_a.is_active.return_value = False
1452 1
        mock_link.is_active.return_value = False
1453 1
        mock_interface.link = mock_link
1454 1
        self.napp.handle_link_up(mock_interface)
1455 1
        mock_interface.activate.assert_not_called()
1456 1
        assert mock_topology_update.call_count == 1
1457 1
        mock_status_change.assert_not_called()
1458
1459 1
    @patch('napps.kytos.topology.main.Main._get_link_or_create')
1460 1
    @patch('napps.kytos.topology.main.Main.notify_link_up_if_status')
1461 1
    def test_add_links(self, *args):
1462
        """Test add_links."""
1463 1
        (mock_notify_link_up_if_status,
1464
         mock_get_link_or_create) = args
1465
1466 1
        mock_link = MagicMock()
1467 1
        mock_get_link_or_create.return_value = (mock_link, True)
1468 1
        mock_event = MagicMock()
1469 1
        mock_intf_a = MagicMock()
1470 1
        mock_intf_b = MagicMock()
1471 1
        mock_event.content = {
1472
            "interface_a": mock_intf_a,
1473
            "interface_b": mock_intf_b
1474
        }
1475 1
        self.napp.add_links(mock_event)
1476 1
        assert mock_link.id in self.napp.link_status_change
1477 1
        mock_get_link_or_create.assert_called()
1478 1
        mock_notify_link_up_if_status.assert_called()
1479 1
        mock_intf_a.update_link.assert_called()
1480 1
        mock_intf_b.update_link.assert_called()
1481 1
        mock_link.endpoint_a = mock_intf_a
1482 1
        mock_link.endpoint_b = mock_intf_b
1483
1484 1
    def test_notify_switch_enabled(self):
1485
        """Test notify switch enabled."""
1486 1
        dpid = "00:00:00:00:00:00:00:01"
1487 1
        mock_buffers_put = MagicMock()
1488 1
        self.napp.controller.buffers.app.put = mock_buffers_put
1489 1
        self.napp.notify_switch_enabled(dpid)
1490 1
        mock_buffers_put.assert_called()
1491
1492 1
    def test_notify_switch_disabled(self):
1493
        """Test notify switch disabled."""
1494 1
        dpid = "00:00:00:00:00:00:00:01"
1495 1
        mock_buffers_put = MagicMock()
1496 1
        self.napp.controller.buffers.app.put = mock_buffers_put
1497 1
        self.napp.notify_switch_disabled(dpid)
1498 1
        mock_buffers_put.assert_called()
1499
1500 1
    def test_notify_topology_update(self):
1501
        """Test notify_topology_update."""
1502 1
        mock_buffers_put = MagicMock()
1503 1
        self.napp.controller.buffers.app.put = mock_buffers_put
1504 1
        self.napp.notify_topology_update()
1505 1
        mock_buffers_put.assert_called()
1506
1507 1
    def test_notify_link_status_change(self):
1508
        """Test notify link status change."""
1509 1
        mock_buffers_put = MagicMock()
1510 1
        self.napp.controller.buffers.app.put = mock_buffers_put
1511 1
        mock_link = create_autospec(Link)
1512 1
        mock_link.id = 'test_link'
1513 1
        mock_link.status_reason = frozenset()
1514 1
        mock_link.status = EntityStatus.UP
1515
1516
        # Check when switching to up
1517 1
        self.napp.notify_link_status_change(mock_link, 'test')
1518 1
        assert mock_buffers_put.call_count == 1
1519 1
        args, _ = mock_buffers_put.call_args
1520 1
        event = args[0]
1521 1
        assert event.content['link'] is mock_link
1522 1
        assert event.content['reason'] == 'test'
1523 1
        assert event.name == 'kytos/topology.link_up'
1524
1525
        # Check result when no change
1526 1
        self.napp.notify_link_status_change(mock_link, 'test2')
1527 1
        assert mock_buffers_put.call_count == 1
1528
1529
        # Check when switching to down
1530 1
        mock_link.status_reason = frozenset({'disabled'})
1531 1
        mock_link.status = EntityStatus.DOWN
1532 1
        self.napp.notify_link_status_change(mock_link, 'test3')
1533 1
        assert mock_buffers_put.call_count == 2
1534 1
        args, _ = mock_buffers_put.call_args
1535 1
        event = args[0]
1536 1
        assert event.content['link'] is mock_link
1537 1
        assert event.content['reason'] == 'test3'
1538 1
        assert event.name == 'kytos/topology.link_down'
1539
1540 1
    def test_notify_metadata_changes(self):
1541
        """Test notify metadata changes."""
1542 1
        mock_buffers_put = MagicMock()
1543 1
        self.napp.controller.buffers.app.put = mock_buffers_put
1544 1
        count = 0
1545 1
        for spec in [Switch, Interface, Link]:
1546 1
            mock_obj = create_autospec(spec)
1547 1
            mock_obj.metadata = {"some_key": "some_value"}
1548 1
            self.napp.notify_metadata_changes(mock_obj, 'added')
1549 1
            assert mock_buffers_put.call_count == count+1
1550 1
            count += 1
1551 1
        with pytest.raises(ValueError):
1552 1
            self.napp.notify_metadata_changes(MagicMock(), 'added')
1553
1554 1
    def test_notify_port_created(self):
1555
        """Test notify port created."""
1556 1
        mock_buffers_put = MagicMock()
1557 1
        self.napp.controller.buffers.app.put = mock_buffers_put
1558 1
        event = KytosEvent("some_event")
1559 1
        expected_name = "kytos/topology.port.created"
1560 1
        self.napp.notify_port_created(event)
1561 1
        assert mock_buffers_put.call_count == 1
1562 1
        assert mock_buffers_put.call_args_list[0][0][0].name == expected_name
1563
1564 1
    def test_get_links_from_interfaces(self) -> None:
1565
        """Test get_links_from_interfaces."""
1566 1
        interfaces = [MagicMock(id=f"intf{n}") for n in range(4)]
1567 1
        links = {
1568
            "link1": MagicMock(id="link1",
1569
                               endpoint_a=interfaces[0],
1570
                               endpoint_b=interfaces[1]),
1571
            "link2": MagicMock(id="link2",
1572
                               endpoint_a=interfaces[2],
1573
                               endpoint_b=interfaces[3]),
1574
        }
1575 1
        self.napp.links = links
1576 1
        response = self.napp.get_links_from_interfaces(interfaces)
1577 1
        assert links == response
1578 1
        response = self.napp.get_links_from_interfaces(interfaces[:2])
1579 1
        assert response == {"link1": links["link1"]}
1580
1581 1
    def test_handle_link_liveness_disabled(self) -> None:
1582
        """Test handle_link_liveness_disabled."""
1583 1
        interfaces = [MagicMock(id=f"intf{n}") for n in range(4)]
1584 1
        links = {
1585
            "link1": MagicMock(id="link1",
1586
                               endpoint_a=interfaces[0],
1587
                               endpoint_b=interfaces[1]),
1588
            "link2": MagicMock(id="link2",
1589
                               endpoint_a=interfaces[2],
1590
                               endpoint_b=interfaces[3]),
1591
        }
1592 1
        self.napp.links = links
1593 1
        self.napp.notify_topology_update = MagicMock()
1594 1
        self.napp.notify_link_status_change = MagicMock()
1595
1596 1
        self.napp.handle_link_liveness_disabled(interfaces)
1597
1598 1
        assert self.napp.notify_topology_update.call_count == 1
1599 1
        assert self.napp.notify_link_status_change.call_count == len(links)
1600
1601 1
    def test_link_status_hook_link_up_timer(self) -> None:
1602
        """Test status hook link up timer."""
1603 1
        last_change = time.time() - self.napp.link_up_timer + 5
1604 1
        link = MagicMock(metadata={"last_status_change": last_change})
1605 1
        self.napp.link_status_change[link.id] = {
1606
            "last_status_change": last_change,
1607
        }
1608 1
        link.is_active.return_value = True
1609 1
        link.is_enabled.return_value = True
1610 1
        res = self.napp.link_status_hook_link_up_timer(link)
1611 1
        assert res == EntityStatus.DOWN
1612
1613 1
        last_change = time.time() - self.napp.link_up_timer
1614 1
        self.napp.link_status_change[link.id] = {
1615
            "last_status_change": last_change,
1616
        }
1617 1
        res = self.napp.link_status_hook_link_up_timer(link)
1618 1
        assert res is None
1619
1620 1
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1621 1
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1622 1
    @patch('time.sleep')
1623 1
    def test_notify_link_up_if_status(
1624
        self,
1625
        mock_sleep,
1626
        mock_notify_topo,
1627
        mock_notify_link,
1628
    ) -> None:
1629
        """Test notify link up if status."""
1630
1631 1
        link = MagicMock(status=EntityStatus.UP)
1632 1
        self.napp.link_status_change[link.id] = {
1633
            "notified_up_at": now(),
1634
        }
1635 1
        assert not self.napp.notify_link_up_if_status(link, "link up")
1636 1
        link.update_metadata.assert_not_called()
1637 1
        mock_notify_topo.assert_not_called()
1638 1
        mock_notify_link.assert_not_called()
1639
1640 1
        link = MagicMock(status=EntityStatus.UP)
1641 1
        orig_time = now() - timedelta(seconds=60)
1642 1
        self.napp.link_status_change[link.id] = {
1643
            "notified_up_at": orig_time,
1644
        }
1645 1
        assert not self.napp.notify_link_up_if_status(link, "link up")
1646 1
        link_status_info = self.napp.link_status_change[link.id]
1647 1
        new_time = link_status_info["notified_up_at"]
1648 1
        assert new_time != orig_time
1649 1
        mock_notify_topo.assert_called()
1650 1
        mock_notify_link.assert_called()
1651
1652 1
        assert mock_sleep.call_count == 2
1653
1654 1
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1655 1
    def test_notify_switch_links_status(self, mock_notify_link_status_change):
1656
        """Test switch links notification when switch status change"""
1657 1
        buffers_app_mock = MagicMock()
1658 1
        self.napp.controller.buffers.app = buffers_app_mock
1659 1
        dpid = "00:00:00:00:00:00:00:01"
1660 1
        mock_switch = get_switch_mock(dpid)
1661 1
        link1 = MagicMock()
1662 1
        link1.endpoint_a.switch = mock_switch
1663 1
        self.napp.links = {1: link1}
1664
1665 1
        self.napp.notify_switch_links_status(mock_switch, "link enabled")
1666 1
        assert self.napp.controller.buffers.app.put.call_count == 1
1667
1668 1
        self.napp.notify_switch_links_status(mock_switch, "link disabled")
1669 1
        assert self.napp.controller.buffers.app.put.call_count == 1
1670 1
        assert mock_notify_link_status_change.call_count == 1
1671
1672
        # Without notification
1673 1
        link1.endpoint_a.switch = None
1674 1
        self.napp.notify_switch_links_status(mock_switch, "link enabled")
1675 1
        assert self.napp.controller.buffers.app.put.call_count == 1
1676
1677 1
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1678 1
    def test_notify_interface_link_status(self, args):
1679
        """Test interface links notification when enable"""
1680 1
        mock_notify_link_status_change = args
1681 1
        buffers_app_mock = MagicMock()
1682 1
        self.napp.controller.buffers.app = buffers_app_mock
1683 1
        mock_intf = MagicMock(link=MagicMock())
1684 1
        self.napp.notify_interface_link_status(mock_intf, "link enabled")
1685 1
        assert self.napp.controller.buffers.app.put.call_count == 1
1686
1687 1
        self.napp.notify_interface_link_status(mock_intf, "link disabled")
1688 1
        assert mock_notify_link_status_change.call_count == 1
1689 1
        assert self.napp.controller.buffers.app.put.call_count == 1
1690
1691
        # Without notification
1692 1
        mock_intf.link = None
1693 1
        self.napp.notify_interface_link_status(mock_intf, "link enabled")
1694 1
        assert self.napp.controller.buffers.app.put.call_count == 1
1695
1696 1 View Code Duplication
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1697 1
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1698 1
    def test_interruption_start(
1699
        self,
1700
        mock_notify_link_status_change,
1701
        mock_notify_topology_update
1702
    ):
1703
        """Tests processing of received interruption start events."""
1704 1
        link_a = MagicMock()
1705 1
        link_b = MagicMock()
1706 1
        link_c = MagicMock()
1707 1
        self.napp.links = {
1708
            'link_a': link_a,
1709
            'link_b': link_b,
1710
            'link_c': link_c,
1711
        }
1712 1
        event = KytosEvent(
1713
            "topology.interruption.start",
1714
            {
1715
                'type': 'test_interruption',
1716
                'switches': [
1717
                ],
1718
                'interfaces': [
1719
                ],
1720
                'links': [
1721
                    'link_a',
1722
                    'link_c',
1723
                ],
1724
            }
1725
        )
1726 1
        self.napp.handle_interruption_start(event)
1727 1
        mock_notify_link_status_change.assert_has_calls(
1728
            [
1729
                call(link_a, 'test_interruption'),
1730
                call(link_c, 'test_interruption'),
1731
            ]
1732
        )
1733 1
        assert mock_notify_link_status_change.call_count == 2
1734 1
        mock_notify_topology_update.assert_called_once()
1735
1736 1 View Code Duplication
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1737 1
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1738 1
    def test_interruption_end(
1739
        self,
1740
        mock_notify_link_status_change,
1741
        mock_notify_topology_update
1742
    ):
1743
        """Tests processing of received interruption end events."""
1744 1
        link_a = MagicMock()
1745 1
        link_b = MagicMock()
1746 1
        link_c = MagicMock()
1747 1
        self.napp.links = {
1748
            'link_a': link_a,
1749
            'link_b': link_b,
1750
            'link_c': link_c,
1751
        }
1752 1
        event = KytosEvent(
1753
            "topology.interruption.start",
1754
            {
1755
                'type': 'test_interruption',
1756
                'switches': [
1757
                ],
1758
                'interfaces': [
1759
                ],
1760
                'links': [
1761
                    'link_a',
1762
                    'link_c',
1763
                ],
1764
            }
1765
        )
1766 1
        self.napp.handle_interruption_end(event)
1767 1
        mock_notify_link_status_change.assert_has_calls(
1768
            [
1769
                call(link_a, 'test_interruption'),
1770
                call(link_c, 'test_interruption'),
1771
            ]
1772
        )
1773 1
        assert mock_notify_link_status_change.call_count == 2
1774 1
        mock_notify_topology_update.assert_called_once()
1775
1776 1
    async def test_set_tag_range(self):
1777
        """Test set_tag_range"""
1778 1
        self.napp.controller.loop = asyncio.get_running_loop()
1779 1
        interface_id = '00:00:00:00:00:00:00:01:1'
1780 1
        dpid = '00:00:00:00:00:00:00:01'
1781 1
        mock_switch = get_switch_mock(dpid)
1782 1
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
1783 1
        mock_interface.set_tag_ranges = MagicMock()
1784 1
        self.napp.handle_on_interface_tags = MagicMock()
1785 1
        self.napp.controller.get_interface_by_id = MagicMock()
1786 1
        self.napp.controller.get_interface_by_id.return_value = mock_interface
1787 1
        payload = {
1788
            "tag_type": "vlan",
1789
            "tag_ranges": [[20, 20], [200, 3000]]
1790
        }
1791 1
        url = f"{self.base_endpoint}/interfaces/{interface_id}/tag_ranges"
1792 1
        response = await self.api_client.post(url, json=payload)
1793 1
        assert response.status_code == 200
1794
1795 1
        args = mock_interface.set_tag_ranges.call_args[0]
1796 1
        assert args[0] == payload['tag_ranges']
1797 1
        assert args[1] == payload['tag_type']
1798 1
        assert self.napp.handle_on_interface_tags.call_count == 1
1799
1800 1
    async def test_set_tag_range_not_found(self):
1801
        """Test set_tag_range. Not found"""
1802 1
        self.napp.controller.loop = asyncio.get_running_loop()
1803 1
        interface_id = '00:00:00:00:00:00:00:01:1'
1804 1
        self.napp.controller.get_interface_by_id = MagicMock()
1805 1
        self.napp.controller.get_interface_by_id.return_value = None
1806 1
        payload = {
1807
            "tag_type": "vlan",
1808
            "tag_ranges": [[20, 20], [200, 3000]]
1809
        }
1810 1
        url = f"{self.base_endpoint}/interfaces/{interface_id}/tag_ranges"
1811 1
        response = await self.api_client.post(url, json=payload)
1812
        assert response.status_code == 404
1813
1814 1 View Code Duplication
    async def test_set_tag_range_tag_error(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1815
        """Test set_tag_range TagRangeError"""
1816 1
        self.napp.controller.loop = asyncio.get_running_loop()
1817 1
        interface_id = '00:00:00:00:00:00:00:01:1'
1818 1
        dpid = '00:00:00:00:00:00:00:01'
1819 1
        mock_switch = get_switch_mock(dpid)
1820 1
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
1821 1
        mock_interface.set_tag_ranges = MagicMock()
1822 1
        mock_interface.set_tag_ranges.side_effect = KytosSetTagRangeError("")
1823 1
        mock_interface.notify_interface_tags = MagicMock()
1824 1
        self.napp.controller.get_interface_by_id = MagicMock()
1825 1
        self.napp.controller.get_interface_by_id.return_value = mock_interface
1826 1
        payload = {
1827
            "tag_type": "vlan",
1828
            "tag_ranges": [[20, 20], [200, 3000]]
1829
        }
1830 1
        url = f"{self.base_endpoint}/interfaces/{interface_id}/tag_ranges"
1831 1
        response = await self.api_client.post(url, json=payload)
1832
        assert response.status_code == 400
1833
        assert mock_interface.notify_interface_tags.call_count == 0
1834
1835 1 View Code Duplication
    async def test_set_tag_range_type_error(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1836
        """Test set_tag_range TagRangeError"""
1837 1
        self.napp.controller.loop = asyncio.get_running_loop()
1838 1
        interface_id = '00:00:00:00:00:00:00:01:1'
1839 1
        dpid = '00:00:00:00:00:00:00:01'
1840 1
        mock_switch = get_switch_mock(dpid)
1841 1
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
1842 1
        mock_interface.set_tag_ranges = MagicMock()
1843 1
        mock_interface.set_tag_ranges.side_effect = KytosTagtypeNotSupported(
1844
            ""
1845
        )
1846 1
        self.napp.handle_on_interface_tags = MagicMock()
1847 1
        self.napp.controller.get_interface_by_id = MagicMock()
1848 1
        self.napp.controller.get_interface_by_id.return_value = mock_interface
1849 1
        payload = {
1850
            "tag_type": "wrong_tag_type",
1851
            "tag_ranges": [[20, 20], [200, 3000]]
1852
        }
1853 1
        url = f"{self.base_endpoint}/interfaces/{interface_id}/tag_ranges"
1854 1
        response = await self.api_client.post(url, json=payload)
1855
        assert response.status_code == 400
1856
        assert self.napp.handle_on_interface_tags.call_count == 0
1857
1858 1 View Code Duplication
    async def test_delete_tag_range(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1859
        """Test delete_tag_range"""
1860 1
        self.napp.controller.loop = asyncio.get_running_loop()
1861 1
        interface_id = '00:00:00:00:00:00:00:01:1'
1862 1
        dpid = '00:00:00:00:00:00:00:01'
1863 1
        mock_switch = get_switch_mock(dpid)
1864 1
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
1865 1
        mock_interface.remove_tag_ranges = MagicMock()
1866 1
        self.napp.handle_on_interface_tags = MagicMock()
1867 1
        self.napp.controller.get_interface_by_id = MagicMock()
1868 1
        self.napp.controller.get_interface_by_id.return_value = mock_interface
1869 1
        url = f"{self.base_endpoint}/interfaces/{interface_id}/tag_ranges"
1870 1
        response = await self.api_client.delete(url)
1871 1
        assert response.status_code == 200
1872 1
        assert mock_interface.remove_tag_ranges.call_count == 1
1873
1874 1 View Code Duplication
    async def test_delete_tag_range_not_found(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1875
        """Test delete_tag_range. Not found"""
1876 1
        self.napp.controller.loop = asyncio.get_running_loop()
1877 1
        interface_id = '00:00:00:00:00:00:00:01:1'
1878 1
        dpid = '00:00:00:00:00:00:00:01'
1879 1
        mock_switch = get_switch_mock(dpid)
1880 1
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
1881 1
        mock_interface.remove_tag_ranges = MagicMock()
1882 1
        self.napp.controller.get_interface_by_id = MagicMock()
1883 1
        self.napp.controller.get_interface_by_id.return_value = None
1884 1
        url = f"{self.base_endpoint}/interfaces/{interface_id}/tag_ranges"
1885 1
        response = await self.api_client.delete(url)
1886
        assert response.status_code == 404
1887
        assert mock_interface.remove_tag_ranges.call_count == 0
1888
1889 1
    async def test_delete_tag_range_type_error(self):
1890
        """Test delete_tag_range TagRangeError"""
1891 1
        self.napp.controller.loop = asyncio.get_running_loop()
1892 1
        interface_id = '00:00:00:00:00:00:00:01:1'
1893 1
        dpid = '00:00:00:00:00:00:00:01'
1894 1
        mock_switch = get_switch_mock(dpid)
1895 1
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
1896 1
        mock_interface.remove_tag_ranges = MagicMock()
1897 1
        remove_tag = mock_interface.remove_tag_ranges
1898 1
        remove_tag.side_effect = KytosTagtypeNotSupported("")
1899 1
        self.napp.controller.get_interface_by_id = MagicMock()
1900 1
        self.napp.controller.get_interface_by_id.return_value = mock_interface
1901 1
        url = f"{self.base_endpoint}/interfaces/{interface_id}/tag_ranges"
1902 1
        response = await self.api_client.delete(url)
1903
        assert response.status_code == 400
1904
1905 1 View Code Duplication
    async def test_get_all_tag_ranges(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1906
        """Test get_all_tag_ranges"""
1907 1
        self.napp.controller.loop = asyncio.get_running_loop()
1908 1
        dpid = '00:00:00:00:00:00:00:01'
1909 1
        switch = get_switch_mock(dpid)
1910 1
        interface = get_interface_mock('s1-eth1', 1, switch)
1911 1
        tags = {'vlan': [[1, 4095]]}
1912 1
        special_tags = {'vlan': ["vlan"]}
1913 1
        interface.tag_ranges = tags
1914 1
        interface.available_tags = tags
1915 1
        interface.special_available_tags = special_tags
1916 1
        interface.special_tags = special_tags
1917 1
        switch.interfaces = {1: interface}
1918 1
        self.napp.controller.switches = {dpid: switch}
1919 1
        url = f"{self.base_endpoint}/interfaces/tag_ranges"
1920 1
        response = await self.api_client.get(url)
1921 1
        expected = {dpid + ":1": {
1922
            'available_tags': tags,
1923
            'tag_ranges': tags,
1924
            'special_available_tags': special_tags,
1925
            'special_tags': special_tags
1926
        }}
1927 1
        assert response.status_code == 200
1928 1
        assert response.json() == expected
1929
1930 1 View Code Duplication
    async def test_get_tag_ranges_by_intf(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1931
        """Test get_tag_ranges_by_intf"""
1932 1
        self.napp.controller.loop = asyncio.get_running_loop()
1933 1
        dpid = '00:00:00:00:00:00:00:01'
1934 1
        switch = get_switch_mock(dpid)
1935 1
        interface = get_interface_mock('s1-eth1', 1, switch)
1936 1
        tags = {'vlan': [[1, 4095]]}
1937 1
        special_tags = {'vlan': ["vlan"]}
1938 1
        interface.tag_ranges = tags
1939 1
        interface.available_tags = tags
1940 1
        interface.special_available_tags = special_tags
1941 1
        interface.special_tags = special_tags
1942 1
        self.napp.controller.get_interface_by_id = MagicMock()
1943 1
        self.napp.controller.get_interface_by_id.return_value = interface
1944 1
        url = f"{self.base_endpoint}/interfaces/{dpid}:1/tag_ranges"
1945 1
        response = await self.api_client.get(url)
1946 1
        expected = {
1947
            '00:00:00:00:00:00:00:01:1': {
1948
                "available_tags": tags,
1949
                "tag_ranges": tags,
1950
                'special_available_tags': special_tags,
1951
                'special_tags': special_tags
1952
            }
1953
        }
1954 1
        assert response.status_code == 200
1955 1
        assert response.json() == expected
1956
1957 1
    async def test_get_tag_ranges_by_intf_error(self):
1958
        """Test get_tag_ranges_by_intf with NotFound"""
1959 1
        self.napp.controller.loop = asyncio.get_running_loop()
1960 1
        dpid = '00:00:00:00:00:00:00:01'
1961 1
        self.napp.controller.get_interface_by_id = MagicMock()
1962 1
        self.napp.controller.get_interface_by_id.return_value = None
1963 1
        url = f"{self.base_endpoint}/interfaces/{dpid}:1/tag_ranges"
1964 1
        response = await self.api_client.get(url)
1965
        assert response.status_code == 404
1966
1967 1
    async def test_set_special_tags(self):
1968
        """Test set_special_tags"""
1969 1
        self.napp.controller.loop = asyncio.get_running_loop()
1970 1
        interface_id = '00:00:00:00:00:00:00:01:1'
1971 1
        dpid = '00:00:00:00:00:00:00:01'
1972 1
        mock_switch = get_switch_mock(dpid)
1973 1
        mock_intf = get_interface_mock('s1-eth1', 1, mock_switch)
1974 1
        mock_intf.set_special_tags = MagicMock()
1975 1
        self.napp.handle_on_interface_tags = MagicMock()
1976 1
        self.napp.controller.get_interface_by_id = MagicMock()
1977 1
        self.napp.controller.get_interface_by_id.return_value = mock_intf
1978 1
        payload = {
1979
            "tag_type": "vlan",
1980
            "special_tags": ["untagged"],
1981
        }
1982 1
        url = f"{self.base_endpoint}/interfaces/{interface_id}/"\
1983
              "special_tags"
1984 1
        response = await self.api_client.post(url, json=payload)
1985 1
        assert response.status_code == 200
1986
1987 1
        args = mock_intf.set_special_tags.call_args[0]
1988 1
        assert args[0] == payload["tag_type"]
1989 1
        assert args[1] == payload['special_tags']
1990 1
        assert self.napp.handle_on_interface_tags.call_count == 1
1991
1992
        # KytosTagError
1993 1
        mock_intf.set_special_tags.side_effect = KytosTagtypeNotSupported("")
1994 1
        url = f"{self.base_endpoint}/interfaces/{interface_id}/"\
1995
              "special_tags"
1996 1
        response = await self.api_client.post(url, json=payload)
1997
        assert response.status_code == 400
1998
        assert self.napp.handle_on_interface_tags.call_count == 1
1999
2000
        # Interface Not Found
2001
        self.napp.controller.get_interface_by_id.return_value = None
2002
        url = f"{self.base_endpoint}/interfaces/{interface_id}/"\
2003
              "special_tags"
2004
        response = await self.api_client.post(url, json=payload)
2005
        assert response.status_code == 404
2006
        assert self.napp.handle_on_interface_tags.call_count == 1
2007
2008 1
    async def test_delete_link(self):
2009
        """Test delete_link"""
2010 1
        dpid_a = '00:00:00:00:00:00:00:01'
2011 1
        dpid_b = '00:00:00:00:00:00:00:02'
2012 1
        link_id = 'mock_link'
2013 1
        mock_switch_a = get_switch_mock(dpid_a)
2014 1
        mock_switch_b = get_switch_mock(dpid_b)
2015 1
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
2016 1
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
2017 1
        mock_link = get_link_mock(mock_interface_a, mock_interface_b)
2018 1
        mock_link.id = link_id
2019 1
        mock_link.status = EntityStatus.DISABLED
2020 1
        mock_interface_a.link = mock_link
2021 1
        mock_interface_b.link = mock_link
2022 1
        self.napp.links = {link_id: mock_link}
2023
2024 1
        call_count = self.napp.controller.buffers.app.put.call_count
2025 1
        endpoint = f"{self.base_endpoint}/links/{link_id}"
2026 1
        response = await self.api_client.delete(endpoint)
2027 1
        assert response.status_code == 200
2028 1
        assert self.napp.topo_controller.delete_link.call_count == 1
2029 1
        assert len(self.napp.links) == 0
2030 1
        call_count += 2
2031 1
        assert self.napp.controller.buffers.app.put.call_count == call_count
2032
2033
        # Link is up
2034 1
        self.napp.links = {link_id: mock_link}
2035 1
        mock_link.status = EntityStatus.UP
2036 1
        endpoint = f"{self.base_endpoint}/links/{link_id}"
2037 1
        response = await self.api_client.delete(endpoint)
2038
        assert response.status_code == 409
2039
        assert self.napp.topo_controller.delete_link.call_count == 1
2040
        assert self.napp.controller.buffers.app.put.call_count == call_count
2041
2042
        # Link does not exist
2043
        del self.napp.links[link_id]
2044
        endpoint = f"{self.base_endpoint}/links/{link_id}"
2045
        response = await self.api_client.delete(endpoint)
2046
        assert response.status_code == 404
2047
        assert self.napp.topo_controller.delete_link.call_count == 1
2048
        assert self.napp.controller.buffers.app.put.call_count == call_count
2049
2050 1
    @patch('napps.kytos.topology.main.Main.get_flows_by_switch')
2051 1
    async def test_delete_switch(self, mock_get):
2052
        """Test delete_switch"""
2053
        # Error 404 NotFound
2054 1
        dpid = '00:00:00:00:00:00:00:01'
2055 1
        endpoint = f"{self.base_endpoint}/switches/{dpid}"
2056 1
        response = await self.api_client.delete(endpoint)
2057
        assert response.status_code == 404, response
2058
2059
        # Error 409 Switch not disabled
2060
        mock_switch = get_switch_mock(dpid)
2061
        mock_switch.status = EntityStatus.UP
2062
        self.napp.controller.switches = {dpid: mock_switch}
2063
        endpoint = f"{self.base_endpoint}/switches/{dpid}"
2064
        response = await self.api_client.delete(endpoint)
2065
        assert response.status_code == 409, response
2066
2067
        # Error 409 Interface vlan is being used
2068 1
        mock_intf = MagicMock(all_tags_available=lambda: False)
2069
        mock_switch.interfaces = {1: mock_intf}
2070
        mock_switch.status = EntityStatus.DISABLED
2071
        endpoint = f"{self.base_endpoint}/switches/{dpid}"
2072
        response = await self.api_client.delete(endpoint)
2073
        assert response.status_code == 409, response
2074
2075
        # Error 409 Swith have links
2076 1
        mock_switch.interfaces[1].all_tags_available = lambda: True
2077
        mock_switch_2 = get_switch_mock("00:00:00:00:00:00:00:02")
2078
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch)
2079
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_2)
2080
        mock_link = get_link_mock(mock_interface_a, mock_interface_b)
2081
        self.napp.links = {'0e2b5d7bc858b9f38db11b69': mock_link}
2082
        endpoint = f"{self.base_endpoint}/switches/{dpid}"
2083
        response = await self.api_client.delete(endpoint)
2084
        assert response.status_code == 409, response
2085
2086
        # Error 409 Switch has flows
2087
        mock_get.return_value = {dpid: {}}
2088
        endpoint = f"{self.base_endpoint}/switches/{dpid}"
2089
        response = await self.api_client.delete(endpoint)
2090
        assert response.status_code == 409, response
2091
2092
        # Success 202
2093
        mock_get.return_value = {}
2094
        self.napp.links = {}
2095
        endpoint = f"{self.base_endpoint}/switches/{dpid}"
2096
        response = await self.api_client.delete(endpoint)
2097 1
        assert response.status_code == 200, response
2098
2099 1
    def test_notify_link_status(self):
2100
        """Test notify_link_enabled_state"""
2101 1
        self.napp.controller.buffers.app.put.reset_mock()
2102 1
        link = Mock()
2103 1
        link.id = 'mock_link'
2104 1
        self.napp.notify_link_enabled_state(link, 'enabled')
2105 1
        assert self.napp.controller.buffers.app.put.call_count == 1
2106
2107 1
        self.napp.notify_link_enabled_state(link, 'disabled')
2108 1
        assert self.napp.controller.buffers.app.put.call_count == 2
2109
2110 1
    @patch('napps.kytos.topology.main.tenacity.nap.time')
2111 1
    @patch('httpx.get')
2112 1
    def test_get_flows_by_switch(self, mock_get, _):
2113
        """Test get_flows_by_switch"""
2114 1
        dpid = "00:01"
2115 1
        mock_get.return_value.status_code = 400
2116 1
        with pytest.raises(tenacity.RetryError):
2117 1
            self.napp.get_flows_by_switch(dpid)
2118
2119 1
        mock_get.return_value.status_code = 200
2120 1
        mock_get.return_value.is_server_error = False
2121 1
        expected = {dpid: "mocked_flows"}
2122 1
        mock_get.return_value.json.return_value = expected
2123 1
        actual = self.napp.get_flows_by_switch(dpid)
2124 1
        assert actual == "mocked_flows"
2125
2126 1
    @patch('napps.kytos.topology.main.Main.get_intf_usage')
2127 1
    @patch('napps.kytos.topology.main.Main._delete_interface')
2128 1
    async def test_delete_interface_api(self, mock_delete, mock_usage):
2129
        """Test delete interface API call"""
2130 1
        switch_id = "00:00:00:00:00:00:00:01"
2131 1
        intf_id = "00:00:00:00:00:00:00:01:1"
2132
2133
        # Error 400 Invalid interface id
2134 1
        endpoint = f"{self.base_endpoint}/interfaces/{intf_id}x"
2135 1
        response = await self.api_client.delete(endpoint)
2136
        assert response.status_code == 400, response
2137
2138
        # Error 404 Switch not found
2139
        endpoint = f"{self.base_endpoint}/interfaces/{intf_id}"
2140
        response = await self.api_client.delete(endpoint)
2141
        assert response.status_code == 404, response
2142
2143
        # Error 404 Interface not found
2144
        mock_switch = get_switch_mock(switch_id)
2145
        mock_switch.interfaces = {}
2146
        self.napp.controller.switches = {switch_id: mock_switch}
2147
        response = await self.api_client.delete(endpoint)
2148
        assert response.status_code == 404, response
2149
2150
        # Error 409 Interface is used
2151
        mock_switch.interfaces = {1: Mock()}
2152
        self.napp.controller.switches = {switch_id: mock_switch}
2153
        mock_usage.return_value = "It is enabled or active."
2154
        response = await self.api_client.delete(endpoint)
2155
        assert response.status_code == 409, response
2156
2157
        # Success
2158
        mock_usage.return_value = None
2159
        mock_delete.return_value = True
2160
        response = await self.api_client.delete(endpoint)
2161 1
        assert response.status_code == 200, response
2162
2163 1
    def test_get_intf_usage(self):
2164
        """Test get_intf_usage"""
2165 1
        switch_id = "00:00:00:00:00:00:00:01"
2166 1
        mock_switch = get_switch_mock(switch_id)
2167 1
        mock_intf = get_interface_mock('s1-eth1', 1, mock_switch)
2168
2169 1
        mock_intf.is_enabled.return_value = False
2170 1
        mock_intf.is_active.return_value = True
2171 1
        actual_usage = self.napp.get_intf_usage(mock_intf)
2172 1
        assert actual_usage == "It is enabled or active."
2173
2174 1
        mock_intf.is_active.return_value = False
2175 1
        mock_intf.link = Mock()
2176 1
        actual_usage = self.napp.get_intf_usage(mock_intf)
2177 1
        assert "It has a link," in actual_usage
2178
2179 1
        mock_intf.link = None
2180 1
        self.napp.get_flow_id_by_intf = MagicMock(return_value="mock_flow")
2181 1
        actual_usage = self.napp.get_intf_usage(mock_intf)
2182 1
        assert "There is a flow installed" in actual_usage
2183
2184 1
        self.napp.get_flow_id_by_intf.return_value = None
2185 1
        actual_usage = self.napp.get_intf_usage(mock_intf)
2186 1
        assert actual_usage is None
2187
2188 1
    @patch('napps.kytos.topology.main.Main.get_flows_by_switch')
2189 1
    def test_get_flow_id_by_intf(self, mock_flows):
2190
        """Test get_flow_id_by_intf"""
2191 1
        flows = [
2192
            {
2193
                "flow": {
2194
                    "match": {"in_port": 1, "dl_vlan": 200},
2195
                },
2196
                "flow_id": "flow_0",
2197
            },
2198
            {
2199
                "flow": {
2200
                    "actions": [{"action_type": "output", "port": 1}]
2201
                },
2202
                "flow_id": "flow_1",
2203
            },
2204
            {
2205
                "flow": {
2206
                    "instructions": [{
2207
                        "instruction_type": "apply_actions",
2208
                        "actions": [{"action_type": "output", "port": 1}]
2209
                    }]
2210
                },
2211
                "flow_id": "flow_2",
2212
            },
2213
            {
2214
                "flow": {
2215
                    "match": {"dl_src": "ee:ee:ee:ee:ee:02"},
2216
                },
2217
                "flow_id": "flow_3",
2218
            }
2219
        ]
2220
2221 1
        switch_id = "00:00:00:00:00:00:00:01"
2222 1
        mock_switch = get_switch_mock(switch_id)
2223 1
        mock_intf = get_interface_mock('s1-eth1', 1, mock_switch)
2224
2225 1
        mock_flows.return_value = [flows[0]]
2226 1
        flow_id = self.napp.get_flow_id_by_intf(mock_intf)
2227 1
        assert flow_id == flows[0]["flow_id"]
2228
2229 1
        mock_flows.return_value = [flows[1]]
2230 1
        flow_id = self.napp.get_flow_id_by_intf(mock_intf)
2231 1
        assert flow_id == flows[1]["flow_id"]
2232
2233 1
        mock_flows.return_value = [flows[2]]
2234 1
        flow_id = self.napp.get_flow_id_by_intf(mock_intf)
2235 1
        assert flow_id == flows[2]["flow_id"]
2236
2237 1
        mock_flows.return_value = [flows[3]]
2238 1
        flow_id = self.napp.get_flow_id_by_intf(mock_intf)
2239 1
        assert flow_id is None
2240
2241 1
    def test_delete_interface(self):
2242
        """Test _delete_interface"""
2243 1
        switch_id = "00:00:00:00:00:00:00:01"
2244 1
        mock_switch = get_switch_mock(switch_id)
2245 1
        mock_intf = get_interface_mock('s1-eth1', 1, mock_switch)
2246 1
        self.napp._delete_interface(mock_intf)
2247 1
        assert mock_switch.remove_interface.call_count == 1
2248 1
        assert self.napp.topo_controller.upsert_switch.call_count == 1
2249 1
        delete = self.napp.topo_controller.delete_interface_from_details
2250
        assert delete.call_count == 1
2251