Passed
Pull Request — master (#196)
by Aldo
03:28
created

TestMain.test_notify_switch_links_status()   A

Complexity

Conditions 1

Size

Total Lines 22
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 1

Importance

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