Passed
Push — master ( 0615ef...a9f662 )
by Aldo
05:18 queued 02:41
created

TestMain.test_notify_interface_status()   A

Complexity

Conditions 1

Size

Total Lines 14
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

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