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

TestMain.test_disable_remove_link()   A

Complexity

Conditions 1

Size

Total Lines 25
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 1

Importance

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