Passed
Pull Request — master (#255)
by Aldo
04:46
created

TestMain.test_link_status_mismatched()   A

Complexity

Conditions 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

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