Passed
Pull Request — master (#427)
by
unknown
03:56
created

TestDynamicPathManager.test_clear_path()   A

Complexity

Conditions 1

Size

Total Lines 22
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 18
nop 1
dl 0
loc 22
ccs 4
cts 4
cp 1
crap 1
rs 9.5
c 0
b 0
f 0
1
"""Module to test the Path class."""
2 1
import sys
3 1
from unittest.mock import call, patch, Mock, MagicMock
4 1
import pytest
5 1
from napps.kytos.mef_eline import settings
6
7 1
from kytos.core.common import EntityStatus
8 1
from kytos.core.link import Link
9 1
from kytos.core.switch import Switch
10
11
# pylint: disable=wrong-import-position,ungrouped-imports,no-member
12
13 1
sys.path.insert(0, "/var/lib/kytos/napps/..")
14
# pylint: enable=wrong-import-position
15 1
from napps.kytos.mef_eline.exceptions import InvalidPath  # NOQA pycodestyle
16 1
from napps.kytos.mef_eline.models import (  # NOQA pycodestyle
17
    DynamicPathManager, Path)
18 1
from napps.kytos.mef_eline.tests.helpers import (  # NOQA pycodestyle
19
    MockResponse, get_link_mocked, get_mocked_requests, id_to_interface_mock)
20
21
22 1
class TestPath():
23
    """Class to test path methods."""
24
25 1
    def test_is_affected_by_link_1(self):
26
        """Test method is affected by link."""
27 1
        path = Path()
28 1
        assert path.is_affected_by_link() is False
29
30 1
    def test_link_affected_by_interface_1(self):
31
        """Test method to get the link using an interface."""
32 1
        link1 = Mock()
33 1
        link1.endpoint_a = "a"
34 1
        link1.endpoint_b = "b"
35 1
        link2 = Mock()
36 1
        link2.endpoint_a = "c"
37 1
        link2.endpoint_b = "d"
38 1
        path = Path([link1, link2])
39 1
        assert path.link_affected_by_interface() is None
40
41 1
    def test_link_affected_by_interface_2(self):
42
        """Test method to get the link using an interface."""
43 1
        link1 = Mock()
44 1
        link1.endpoint_a = "a"
45 1
        link1.endpoint_b = "b"
46 1
        link2 = Mock()
47 1
        link2.endpoint_a = "c"
48 1
        link2.endpoint_b = "d"
49 1
        path = Path([link1, link2])
50 1
        assert path.link_affected_by_interface("a") == link1
51
52 1
    def test_status_case_1(self):
53
        """Test if empty link is DISABLED."""
54 1
        current_path = Path()
55 1
        assert current_path.status == EntityStatus.DISABLED
56
57 1
    @patch("requests.get", side_effect=get_mocked_requests)
58 1
    def test_status_case_2(self, requests_mocked):
59
        # pylint: disable=unused-argument
60
        """Test if link status is DOWN."""
61 1
        link1 = get_link_mocked()
62 1
        link2 = get_link_mocked()
63 1
        link1.id = "def"
64 1
        link2.id = "abc"
65 1
        links = [link1, link2]
66 1
        current_path = Path(links)
67 1
        assert current_path.status == EntityStatus.DOWN
68
69 1
    def test_status_case_3(self):
70
        """Test if link status is DISABLED."""
71 1
        links = []
72 1
        current_path = Path(links)
73 1
        assert current_path.status == EntityStatus.DISABLED
74
75
    # This method will be used by the mock to replace requests.get
76 1
    def _mocked_requests_get_status_case_4(self):
77 1
        return MockResponse(
78
            {
79
                "links": {
80
                    "abc": {"active": True, "enabled": True},
81
                    "def": {"active": True, "enabled": True},
82
                }
83
            },
84
            200,
85
        )
86
87 1
    @patch("requests.get", side_effect=_mocked_requests_get_status_case_4)
88 1
    def test_status_case_4(self, requests_mocked):
89
        # pylint: disable=unused-argument
90
        """Test if link status is UP."""
91 1
        link1 = get_link_mocked()
92 1
        link2 = get_link_mocked()
93 1
        link1.id = "def"
94 1
        link2.id = "abc"
95 1
        links = [link1, link2]
96 1
        current_path = Path(links)
97 1
        assert current_path.status == EntityStatus.UP
98
99
    # This method will be used by the mock to replace requests.get
100 1
    def _mocked_requests_get_status_case_5(self):
101 1
        return MockResponse(
102
            {
103
                "links": {
104
                    "abc": {"active": True, "enabled": True},
105
                    "def": {"active": False, "enabled": False},
106
                }
107
            },
108
            200,
109
        )
110
111 1
    @patch("requests.get", side_effect=_mocked_requests_get_status_case_5)
112 1
    def test_status_case_5(self, requests_mocked):
113
        # pylint: disable=unused-argument
114
        """Test if link status is UP."""
115 1
        link1 = get_link_mocked()
116 1
        link2 = get_link_mocked()
117 1
        link1.id = "def"
118 1
        link2.id = "abc"
119 1
        links = [link1, link2]
120 1
        current_path = Path(links)
121 1
        assert current_path.status == EntityStatus.DISABLED
122
123
    # This method will be used by the mock to replace requests.get
124 1
    def _mocked_requests_get_status_case_6(self):
125 1
        return MockResponse(
126
            {
127
                "links": {
128
                    "abc": {"active": False, "enabled": False},
129
                    "def": {"active": False, "enabled": True},
130
                }
131
            },
132
            200,
133
        )
134
135 1
    @patch("requests.get", side_effect=_mocked_requests_get_status_case_6)
136 1
    def test_status_case_6(self, requests_mocked):
137
        # pylint: disable=unused-argument
138
        """Test if link status is UP."""
139 1
        link1 = get_link_mocked()
140 1
        link2 = get_link_mocked()
141 1
        link1.id = "def"
142 1
        link2.id = "abc"
143 1
        links = [link1, link2]
144 1
        current_path = Path(links)
145 1
        assert current_path.status == EntityStatus.DISABLED
146
147 1
    def test_compare_same_paths(self):
148
        """Test compare paths with same links."""
149 1
        links = [
150
            get_link_mocked(
151
                endpoint_a_port=9, endpoint_b_port=10, metadata={"s_vlan": 5}
152
            ),
153
            get_link_mocked(
154
                endpoint_a_port=11, endpoint_b_port=12, metadata={"s_vlan": 6}
155
            ),
156
        ]
157
158 1
        path_1 = Path(links)
159 1
        path_2 = Path(links)
160 1
        assert path_1 == path_2
161
162 1
    def test_compare_different_paths(self):
163
        """Test compare paths with different links."""
164 1
        links_1 = [
165
            get_link_mocked(
166
                endpoint_a_port=9, endpoint_b_port=10, metadata={"s_vlan": 5}
167
            ),
168
            get_link_mocked(
169
                endpoint_a_port=11, endpoint_b_port=12, metadata={"s_vlan": 6}
170
            ),
171
        ]
172 1
        links_2 = [
173
            get_link_mocked(
174
                endpoint_a_port=12, endpoint_b_port=11, metadata={"s_vlan": 5}
175
            ),
176
            get_link_mocked(
177
                endpoint_a_port=14, endpoint_b_port=16, metadata={"s_vlan": 11}
178
            ),
179
        ]
180
181 1
        path_1 = Path(links_1)
182 1
        path_2 = Path(links_2)
183 1
        assert path_1 != path_2
184
185 1
    def test_as_dict(self):
186
        """Test path as dict."""
187 1
        links = [
188
            get_link_mocked(link_dict={"id": 3}),
189
            get_link_mocked(link_dict={"id": 2}),
190
        ]
191
192 1
        current_path = Path(links)
193 1
        expected_dict = [{"id": 3}, {"id": 2}]
194 1
        assert expected_dict == current_path.as_dict()
195
196 1
    def test_empty_is_valid(self) -> None:
197
        """Test empty path is valid."""
198 1
        path = Path([])
199 1
        assert path.is_valid(MagicMock(), MagicMock(), False)
200
201 1 View Code Duplication
    def test_is_valid(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
202
        """Test is_valid method."""
203 1
        switch1 = Switch("00:00:00:00:00:00:00:01")
204 1
        switch2 = Switch("00:00:00:00:00:00:00:02")
205 1
        switch3 = Switch("00:00:00:00:00:00:00:03")
206 1
        switch4 = Switch("00:00:00:00:00:00:00:04")
207 1
        switch5 = Switch("00:00:00:00:00:00:00:05")
208 1
        switch6 = Switch("00:00:00:00:00:00:00:06")
209
        # Links connected
210 1
        links = [
211
            get_link_mocked(switch_a=switch5, switch_b=switch6),
212
            get_link_mocked(switch_a=switch4, switch_b=switch5),
213
            get_link_mocked(switch_a=switch3, switch_b=switch4),
214
            get_link_mocked(switch_a=switch2, switch_b=switch3),
215
            get_link_mocked(switch_a=switch1, switch_b=switch2),
216
        ]
217 1
        path = Path(links)
218 1
        assert path.is_valid(switch6, switch1) is True
219
220 1
    def test_is_valid_diconnected(self):
221
        """Test is_valid with disconnected path"""
222 1
        switch1 = Switch("00:00:00:00:00:00:00:01")
223 1
        switch2 = Switch("00:00:00:00:00:00:00:02")
224 1
        switch3 = Switch("00:00:00:00:00:00:00:03")
225 1
        switch4 = Switch("00:00:00:00:00:00:00:04")
226 1
        links = [
227
            get_link_mocked(switch_a=switch1, switch_b=switch2),
228
            get_link_mocked(switch_a=switch3, switch_b=switch4),
229
            get_link_mocked(switch_a=switch2, switch_b=switch4),
230
        ]
231 1
        path = Path(links)
232 1
        with pytest.raises(InvalidPath):
233 1
            path.is_valid(switch1, switch4)
234
235 1
    def test_is_valid_with_loop(self):
236
        """Test is_valid with a loop"""
237 1
        switch1 = Switch("00:00:00:00:00:00:00:01")
238 1
        switch2 = Switch("00:00:00:00:00:00:00:02")
239 1
        switch3 = Switch("00:00:00:00:00:00:00:03")
240 1
        switch4 = Switch("00:00:00:00:00:00:00:04")
241 1
        switch5 = Switch("00:00:00:00:00:00:00:05")
242 1
        links = [
243
            get_link_mocked(switch_a=switch1, switch_b=switch2),
244
            get_link_mocked(switch_a=switch2, switch_b=switch3),
245
            get_link_mocked(switch_a=switch3, switch_b=switch4),
246
            get_link_mocked(switch_a=switch2, switch_b=switch4),
247
            get_link_mocked(switch_a=switch2, switch_b=switch5),
248
        ]
249 1
        path = Path(links)
250 1
        with pytest.raises(InvalidPath):
251 1
            path.is_valid(switch1, switch5)
252
253 1 View Code Duplication
    def test_is_valid_invalid(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
254
        """Test is_valid when path is invalid
255
        UNI_Z is not connected"""
256 1
        switch1 = Switch("00:00:00:00:00:00:00:01")
257 1
        switch2 = Switch("00:00:00:00:00:00:00:02")
258 1
        switch3 = Switch("00:00:00:00:00:00:00:03")
259 1
        switch4 = Switch("00:00:00:00:00:00:00:04")
260 1
        switch5 = Switch("00:00:00:00:00:00:00:05")
261 1
        switch6 = Switch("00:00:00:00:00:00:00:06")
262 1
        links = [
263
            get_link_mocked(switch_a=switch5, switch_b=switch6),
264
            get_link_mocked(switch_a=switch4, switch_b=switch5),
265
            get_link_mocked(switch_a=switch3, switch_b=switch4),
266
            get_link_mocked(switch_a=switch2, switch_b=switch3),
267
            get_link_mocked(switch_a=switch1, switch_b=switch2),
268
        ]
269 1
        path = Path(links)
270 1
        with pytest.raises(InvalidPath):
271 1
            path.is_valid(switch3, switch6)
272
273
274 1
class TestDynamicPathManager():
275
    """Tests for the DynamicPathManager class"""
276
277 1
    def test_clear_path(self):
278
        """Test _clear_path method"""
279 1
        path = [
280
            '00:00:00:00:00:00:00:01:1',
281
            '00:00:00:00:00:00:00:02:3',
282
            '00:00:00:00:00:00:00:02',
283
            '00:00:00:00:00:00:00:02:4',
284
            '00:00:00:00:00:00:00:03:2',
285
            '00:00:00:00:00:00:00:03',
286
            '00:00:00:00:00:00:00:03:1',
287
            '00:00:00:00:00:00:00:04:1'
288
        ]
289 1
        expected_path = [
290
            '00:00:00:00:00:00:00:01:1',
291
            '00:00:00:00:00:00:00:02:3',
292
            '00:00:00:00:00:00:00:02:4',
293
            '00:00:00:00:00:00:00:03:2',
294
            '00:00:00:00:00:00:00:03:1',
295
            '00:00:00:00:00:00:00:04:1'
296
        ]
297
        # pylint: disable=protected-access
298 1
        assert DynamicPathManager._clear_path(path) == expected_path
299
300 1
    def test_create_path_invalid(self):
301
        """Test create_path method"""
302 1
        path = [
303
            '00:00:00:00:00:00:00:01:1',
304
            '00:00:00:00:00:00:00:02:3',
305
            '00:00:00:00:00:00:00:02',
306
            '00:00:00:00:00:00:00:02:4',
307
            '00:00:00:00:00:00:00:03:2',
308
            '00:00:00:00:00:00:00:03',
309
            '00:00:00:00:00:00:00:03:1',
310
        ]
311 1
        assert DynamicPathManager.create_path(path) is None
312
313 1
    @patch("requests.post")
314 1
    def test_get_best_path(self, mock_requests_post):
315
        """Test get_best_path method."""
316 1
        controller = MagicMock()
317 1
        controller.get_interface_by_id.side_effect = id_to_interface_mock
318 1
        DynamicPathManager.set_controller(controller)
319
320 1
        paths1 = {
321
            "paths": [
322
                {
323
                    "cost": 5,
324
                    "hops": [
325
                        "00:00:00:00:00:00:00:01:1",
326
                        "00:00:00:00:00:00:00:01",
327
                        "00:00:00:00:00:00:00:01:2",
328
                        "00:00:00:00:00:00:00:02:2",
329
                        "00:00:00:00:00:00:00:02",
330
                        "00:00:00:00:00:00:00:02:1"
331
                        ]
332
                },
333
            ]
334
        }
335
336 1
        expected_path = [
337
            Link(
338
                id_to_interface_mock("00:00:00:00:00:00:00:01:2"),
339
                id_to_interface_mock("00:00:00:00:00:00:00:02:2")
340
            ),
341
        ]
342
343
        # test success case
344 1
        mock_response = MagicMock()
345 1
        mock_response.status_code = 200
346 1
        mock_response.json.return_value = paths1
347 1
        mock_requests_post.return_value = mock_response
348
349 1
        res_paths = list(DynamicPathManager.get_best_path(MagicMock()))
350 1
        assert (
351
            [link.id for link in res_paths] ==
352
            [link.id for link in expected_path]
353
        )
354
355
        # test failure when controller dont find the interface on create_path
356 1
        controller.get_interface_by_id.side_effect = [
357
            id_to_interface_mock("00:00:00:00:00:00:00:01:2"),
358
            None
359
        ]
360 1
        assert DynamicPathManager.get_best_path(MagicMock()) is None
361
362 1
        mock_response.status_code = 400
363 1
        mock_response.json.return_value = {}
364 1
        mock_requests_post.return_value = mock_response
365
366 1
        res_paths = DynamicPathManager.get_best_path(MagicMock())
367 1
        assert res_paths is None
368
369 1
    @patch("requests.post")
370 1
    def test_get_best_paths(self, mock_requests_post):
371
        """Test get_best_paths method."""
372 1
        controller = MagicMock()
373 1
        controller.get_interface_by_id.side_effect = id_to_interface_mock
374 1
        DynamicPathManager.set_controller(controller)
375
376 1
        paths1 = {
377
            "paths": [
378
                {
379
                    "cost": 5,
380
                    "hops": [
381
                        "00:00:00:00:00:00:00:01:1",
382
                        "00:00:00:00:00:00:00:01",
383
                        "00:00:00:00:00:00:00:01:2",
384
                        "00:00:00:00:00:00:00:02:2",
385
                        "00:00:00:00:00:00:00:02",
386
                        "00:00:00:00:00:00:00:02:1"
387
                        ]
388
                },
389
                {
390
                    "cost": 11,
391
                    "hops": [
392
                        "00:00:00:00:00:00:00:01:1",
393
                        "00:00:00:00:00:00:00:01",
394
                        "00:00:00:00:00:00:00:01:2",
395
                        "00:00:00:00:00:00:00:02:2",
396
                        "00:00:00:00:00:00:00:02",
397
                        "00:00:00:00:00:00:00:02:3",
398
                        "00:00:00:00:00:00:00:03:3",
399
                        "00:00:00:00:00:00:00:03",
400
                        "00:00:00:00:00:00:00:03:4",
401
                        "00:00:00:00:00:00:00:04:4",
402
                        "00:00:00:00:00:00:00:04",
403
                        "00:00:00:00:00:00:00:04:1"
404
                        ]
405
                },
406
            ]
407
        }
408
409 1
        expected_paths_0 = [
410
            Link(
411
                id_to_interface_mock("00:00:00:00:00:00:00:01:2"),
412
                id_to_interface_mock("00:00:00:00:00:00:00:02:2")
413
            ),
414
        ]
415
416 1
        expected_paths_1 = [
417
            Link(
418
                id_to_interface_mock("00:00:00:00:00:00:00:01:2"),
419
                id_to_interface_mock("00:00:00:00:00:00:00:02:2")
420
            ),
421
            Link(
422
                id_to_interface_mock("00:00:00:00:00:00:00:02:3"),
423
                id_to_interface_mock("00:00:00:00:00:00:00:03:3")
424
            ),
425
            Link(
426
                id_to_interface_mock("00:00:00:00:00:00:00:03:4"),
427
                id_to_interface_mock("00:00:00:00:00:00:00:04:4")
428
            ),
429
        ]
430
431 1
        mock_response = MagicMock()
432 1
        mock_response.status_code = 200
433 1
        mock_response.json.return_value = paths1
434 1
        mock_requests_post.return_value = mock_response
435 1
        kwargs = {
436
            "spf_attribute": settings.SPF_ATTRIBUTE,
437
            "spf_max_path_cost": 8,
438
            "mandatory_metrics": {
439
                "ownership": "red"
440
            }
441
        }
442 1
        circuit = MagicMock()
443 1
        circuit.uni_a.interface.id = "1"
444 1
        circuit.uni_z.interface.id = "2"
445 1
        max_paths = 2
446 1
        res_paths = list(DynamicPathManager.get_best_paths(circuit,
447
                         max_paths=max_paths, **kwargs))
448 1
        assert (
449
            [link.id for link in res_paths[0]] ==
450
            [link.id for link in expected_paths_0]
451
        )
452 1
        assert (
453
            [link.id for link in res_paths[1]] ==
454
            [link.id for link in expected_paths_1]
455
        )
456 1
        expected_call = call(
457
            "http://localhost:8181/api/kytos/pathfinder/v3/",
458
            json={
459
                **{
460
                    "source": circuit.uni_a.interface.id,
461
                    "destination": circuit.uni_z.interface.id,
462
                    "spf_max_paths": max_paths,
463
                },
464
                **kwargs
465
            },
466
        )
467 1
        mock_requests_post.assert_has_calls([expected_call])
468
469 1
    @patch("napps.kytos.mef_eline.models.path.log")
470 1
    @patch("requests.post")
471 1
    def test_get_best_paths_error(self, mock_requests_post, mock_log):
472
        """Test get_best_paths method."""
473 1
        controller = MagicMock()
474 1
        DynamicPathManager.set_controller(controller)
475 1
        circuit = MagicMock()
476 1
        circuit.id = "id"
477 1
        circuit.uni_a.interface.id = "1"
478 1
        circuit.uni_z.interface.id = "2"
479 1
        circuit.name = "some_name"
480
481 1
        mock_response = MagicMock()
482 1
        mock_response.status_code = 400
483 1
        mock_response.json.return_value = {}
484 1
        mock_requests_post.return_value = mock_response
485
486 1
        res_paths = list(DynamicPathManager.get_best_paths(circuit))
487 1
        assert not res_paths
488 1
        assert isinstance(res_paths, list)
489 1
        assert mock_log.error.call_count == 1
490
491
    # pylint: disable=too-many-statements, too-many-locals
492 1
    @patch.object(
493
        DynamicPathManager,
494
        "get_shared_components",
495
        side_effect=DynamicPathManager.get_shared_components
496
    )
497 1
    @patch("requests.post")
498 1
    def test_get_disjoint_paths(self, mock_requests_post, mock_shared):
499
        """Test get_disjoint_paths method."""
500
501 1
        controller = MagicMock()
502 1
        controller.get_interface_by_id.side_effect = id_to_interface_mock
503 1
        DynamicPathManager.set_controller(controller)
504
505 1
        evc = MagicMock()
506 1
        evc.secondary_constraints = {
507
            "spf_attribute": "hop",
508
            "spf_max_path_cost": 20,
509
            "mandatory_metrics": {
510
                "ownership": "red"
511
            }
512
        }
513 1
        evc.uni_a.interface.id = "1"
514 1
        evc.uni_z.interface.id = "2"
515 1
        evc.uni_a.interface.switch.id = "00:00:00:00:00:00:00:01"
516 1
        evc.uni_z.interface.switch.id = "00:00:00:00:00:00:00:05"
517
518
        # Topo0
519 1
        paths1 = {
520
            "paths": [
521
                {
522
                    "cost": 11,
523
                    "hops": [
524
                        "00:00:00:00:00:00:00:01:1",
525
                        "00:00:00:00:00:00:00:01",
526
                        "00:00:00:00:00:00:00:01:2",
527
                        "00:00:00:00:00:00:00:02:2",
528
                        "00:00:00:00:00:00:00:02",
529
                        "00:00:00:00:00:00:00:02:3",
530
                        "00:00:00:00:00:00:00:04:2",
531
                        "00:00:00:00:00:00:00:04",
532
                        "00:00:00:00:00:00:00:04:3",
533
                        "00:00:00:00:00:00:00:05:2",
534
                        "00:00:00:00:00:00:00:05",
535
                        "00:00:00:00:00:00:00:05:1"
536
                        ]
537
                },
538
                {
539
                    "cost": 11,
540
                    "hops": [
541
                        "00:00:00:00:00:00:00:01:1",
542
                        "00:00:00:00:00:00:00:01",
543
                        "00:00:00:00:00:00:00:01:3",
544
                        "00:00:00:00:00:00:00:03:2",
545
                        "00:00:00:00:00:00:00:03",
546
                        "00:00:00:00:00:00:00:03:3",
547
                        "00:00:00:00:00:00:00:04:4",
548
                        "00:00:00:00:00:00:00:04",
549
                        "00:00:00:00:00:00:00:04:3",
550
                        "00:00:00:00:00:00:00:05:2",
551
                        "00:00:00:00:00:00:00:05",
552
                        "00:00:00:00:00:00:00:05:1"
553
                        ]
554
                },
555
                {
556
                    "cost": 14,
557
                    "hops": [
558
                        "00:00:00:00:00:00:00:01:1",
559
                        "00:00:00:00:00:00:00:01",
560
                        "00:00:00:00:00:00:00:01:2",
561
                        "00:00:00:00:00:00:00:02:2",
562
                        "00:00:00:00:00:00:00:02",
563
                        "00:00:00:00:00:00:00:02:3",
564
                        "00:00:00:00:00:00:00:04:2",
565
                        "00:00:00:00:00:00:00:04",
566
                        "00:00:00:00:00:00:00:04:5",
567
                        "00:00:00:00:00:00:00:06:2",
568
                        "00:00:00:00:00:00:00:06",
569
                        "00:00:00:00:00:00:00:06:3",
570
                        "00:00:00:00:00:00:00:05:3",
571
                        "00:00:00:00:00:00:00:05",
572
                        "00:00:00:00:00:00:00:05:1"
573
                    ]
574
                },
575
                {
576
                    "cost": 14,
577
                    "hops": [
578
                        "00:00:00:00:00:00:00:01:1",
579
                        "00:00:00:00:00:00:00:01",
580
                        "00:00:00:00:00:00:00:01:3",
581
                        "00:00:00:00:00:00:00:03:2",
582
                        "00:00:00:00:00:00:00:03",
583
                        "00:00:00:00:00:00:00:03:3",
584
                        "00:00:00:00:00:00:00:04:4",
585
                        "00:00:00:00:00:00:00:04",
586
                        "00:00:00:00:00:00:00:04:5",
587
                        "00:00:00:00:00:00:00:06:2",
588
                        "00:00:00:00:00:00:00:06",
589
                        "00:00:00:00:00:00:00:06:3",
590
                        "00:00:00:00:00:00:00:05:3",
591
                        "00:00:00:00:00:00:00:05",
592
                        "00:00:00:00:00:00:00:05:1"
593
                    ]
594
                },
595
                {
596
                    "cost": 17,
597
                    "hops": [
598
                        "00:00:00:00:00:00:00:01:1",
599
                        "00:00:00:00:00:00:00:01",
600
                        "00:00:00:00:00:00:00:01:3",
601
                        "00:00:00:00:00:00:00:03:2",
602
                        "00:00:00:00:00:00:00:03",
603
                        "00:00:00:00:00:00:00:03:3",
604
                        "00:00:00:00:00:00:00:04:4",
605
                        "00:00:00:00:00:00:00:04",
606
                        "00:00:00:00:00:00:00:04:5",
607
                        "00:00:00:00:00:00:00:06:2",
608
                        "00:00:00:00:00:00:00:06",
609
                        "00:00:00:00:00:00:00:06:4",
610
                        "00:00:00:00:00:00:00:07:2",
611
                        "00:00:00:00:00:00:00:07",
612
                        "00:00:00:00:00:00:00:07:3",
613
                        "00:00:00:00:00:00:00:05:4",
614
                        "00:00:00:00:00:00:00:05",
615
                        "00:00:00:00:00:00:00:05:1"
616
                    ]
617
                },
618
            ]
619
        }
620
621 1
        mock_response = MagicMock()
622 1
        mock_response.status_code = 200
623 1
        mock_response.json.return_value = paths1
624
625
        # when we dont have the current_path
626 1
        mock_requests_post.return_value = mock_response
627 1
        disjoint_paths = list(DynamicPathManager.get_disjoint_paths(evc, []))
628 1
        assert not disjoint_paths
629
630 1
        current_path = [
631
            Link(
632
                id_to_interface_mock("00:00:00:00:00:00:00:01:2"),
633
                id_to_interface_mock("00:00:00:00:00:00:00:02:2")
634
            ),
635
            Link(
636
                id_to_interface_mock("00:00:00:00:00:00:00:02:3"),
637
                id_to_interface_mock("00:00:00:00:00:00:00:04:2")
638
            ),
639
            Link(
640
                id_to_interface_mock("00:00:00:00:00:00:00:04:3"),
641
                id_to_interface_mock("00:00:00:00:00:00:00:05:2")
642
            ),
643
        ]
644 1
        path_links = [
645
            ("00:00:00:00:00:00:00:01:2", "00:00:00:00:00:00:00:02:2"),
646
            ("00:00:00:00:00:00:00:02:3", "00:00:00:00:00:00:00:04:2"),
647
            ("00:00:00:00:00:00:00:04:3", "00:00:00:00:00:00:00:05:2")
648
        ]
649 1
        path_switches = {
650
            "00:00:00:00:00:00:00:04",
651
            "00:00:00:00:00:00:00:02"
652
        }
653
654
        # only one path available from pathfinder (precesilly the
655
        # current_path), so the maximum disjoint path will be empty
656 1
        mock_response.json.return_value = {"paths": paths1["paths"][0:1]}
657 1
        mock_requests_post.return_value = mock_response
658 1
        paths = list(DynamicPathManager.get_disjoint_paths(evc, current_path))
659 1
        args = mock_shared.call_args[0]
660 1
        assert args[0] == paths1["paths"][0]
661 1
        assert args[1] == path_links
662 1
        assert args[2] == path_switches
663 1
        assert len(paths) == 0
664
665 1
        expected_disjoint_path = [
666
            Link(
667
                id_to_interface_mock("00:00:00:00:00:00:00:01:3"),
668
                id_to_interface_mock("00:00:00:00:00:00:00:03:2")
669
            ),
670
            Link(
671
                id_to_interface_mock("00:00:00:00:00:00:00:03:3"),
672
                id_to_interface_mock("00:00:00:00:00:00:00:04:4")
673
            ),
674
            Link(
675
                id_to_interface_mock("00:00:00:00:00:00:00:04:5"),
676
                id_to_interface_mock("00:00:00:00:00:00:00:06:2")
677
            ),
678
            Link(
679
                id_to_interface_mock("00:00:00:00:00:00:00:06:3"),
680
                id_to_interface_mock("00:00:00:00:00:00:00:05:3")
681
            ),
682
        ]
683
684
        # there are one alternative path
685 1
        mock_response.json.return_value = paths1
686 1
        mock_requests_post.return_value = mock_response
687 1
        paths = list(DynamicPathManager.get_disjoint_paths(evc, current_path))
688 1
        args = mock_shared.call_args[0]
689 1
        assert args[0] == paths1["paths"][-1]
690 1
        assert args[1] == path_links
691 1
        assert args[2] == {
692
            "00:00:00:00:00:00:00:04",
693
            "00:00:00:00:00:00:00:02"
694
        }
695 1
        assert len(paths) == 4
696
        # for more information on the paths please refer to EP029
697 1
        assert len(paths[0]) == 4  # path S-Z-W-I-D
698 1
        assert len(paths[1]) == 5  # path S-Z-W-I-J-D
699 1
        assert len(paths[2]) == 3  # path S-Z-W-D
700 1
        assert len(paths[3]) == 4  # path S-X-W-I-D
701 1
        assert (
702
            [link.id for link in paths[0]] ==
703
            [link.id for link in expected_disjoint_path]
704
        )
705
706 1
        max_paths = 10
707 1
        expected_call = call(
708
            "http://localhost:8181/api/kytos/pathfinder/v3/",
709
            json={
710
                **{
711
                    "source": evc.uni_a.interface.id,
712
                    "destination": evc.uni_z.interface.id,
713
                    "spf_max_paths": max_paths,
714
                },
715
                **evc.secondary_constraints
716
            },
717
        )
718 1
        assert mock_requests_post.call_count >= 1
719
        # If secondary_constraints are set they are expected to be parametrized
720 1
        mock_requests_post.assert_has_calls([expected_call])
721
722
        # EP029 Topo2
723 1
        evc.uni_a.interface.switch.id = "00:00:00:00:00:00:00:01"
724 1
        evc.uni_z.interface.switch.id = "00:00:00:00:00:00:00:07"
725 1
        paths2 = {
726
            "paths": [
727
                {
728
                    "cost": 14,
729
                    "hops": [
730
                        "00:00:00:00:00:00:00:01:1",
731
                        "00:00:00:00:00:00:00:01",
732
                        "00:00:00:00:00:00:00:01:2",
733
                        "00:00:00:00:00:00:00:02:1",
734
                        "00:00:00:00:00:00:00:02",
735
                        "00:00:00:00:00:00:00:02:2",
736
                        "00:00:00:00:00:00:00:03:1",
737
                        "00:00:00:00:00:00:00:03",
738
                        "00:00:00:00:00:00:00:03:2",
739
                        "00:00:00:00:00:00:00:04:1",
740
                        "00:00:00:00:00:00:00:04",
741
                        "00:00:00:00:00:00:00:04:2",
742
                        "00:00:00:00:00:00:00:07:2",
743
                        "00:00:00:00:00:00:00:07",
744
                        "00:00:00:00:00:00:00:07:1"
745
                    ]
746
                },
747
                {
748
                    "cost": 17,
749
                    "hops": [
750
                        "00:00:00:00:00:00:00:01:1",
751
                        "00:00:00:00:00:00:00:01",
752
                        "00:00:00:00:00:00:00:01:2",
753
                        "00:00:00:00:00:00:00:02:1",
754
                        "00:00:00:00:00:00:00:02",
755
                        "00:00:00:00:00:00:00:02:3",
756
                        "00:00:00:00:00:00:00:05:1",
757
                        "00:00:00:00:00:00:00:05",
758
                        "00:00:00:00:00:00:00:05:2",
759
                        "00:00:00:00:00:00:00:06:1",
760
                        "00:00:00:00:00:00:00:06",
761
                        "00:00:00:00:00:00:00:06:2",
762
                        "00:00:00:00:00:00:00:04:3",
763
                        "00:00:00:00:00:00:00:04",
764
                        "00:00:00:00:00:00:00:04:2",
765
                        "00:00:00:00:00:00:00:07:2",
766
                        "00:00:00:00:00:00:00:07",
767
                        "00:00:00:00:00:00:00:07:1"
768
                    ]
769
                }
770
            ]
771
        }
772
773 1
        current_path = [
774
            Link(
775
                id_to_interface_mock("00:00:00:00:00:00:00:01:2"),
776
                id_to_interface_mock("00:00:00:00:00:00:00:02:1")
777
            ),
778
            Link(
779
                id_to_interface_mock("00:00:00:00:00:00:00:02:2"),
780
                id_to_interface_mock("00:00:00:00:00:00:00:03:1")
781
            ),
782
            Link(
783
                id_to_interface_mock("00:00:00:00:00:00:00:03:2"),
784
                id_to_interface_mock("00:00:00:00:00:00:00:04:1")
785
            ),
786
            Link(
787
                id_to_interface_mock("00:00:00:00:00:00:00:04:2"),
788
                id_to_interface_mock("00:00:00:00:00:00:00:07:2")
789
            ),
790
        ]
791 1
        path_interfaces = [
792
            ("00:00:00:00:00:00:00:01:2", "00:00:00:00:00:00:00:02:1"),
793
            ("00:00:00:00:00:00:00:02:2", "00:00:00:00:00:00:00:03:1"),
794
            ("00:00:00:00:00:00:00:03:2", "00:00:00:00:00:00:00:04:1"),
795
            ("00:00:00:00:00:00:00:04:2", "00:00:00:00:00:00:00:07:2")
796
        ]
797 1
        path_switches = {
798
            "00:00:00:00:00:00:00:02",
799
            "00:00:00:00:00:00:00:03",
800
            "00:00:00:00:00:00:00:04"
801
        }
802
803 1
        expected_disjoint_path = [
804
            Link(
805
                id_to_interface_mock("00:00:00:00:00:00:00:01:2"),
806
                id_to_interface_mock("00:00:00:00:00:00:00:02:1")
807
            ),
808
            Link(
809
                id_to_interface_mock("00:00:00:00:00:00:00:02:3"),
810
                id_to_interface_mock("00:00:00:00:00:00:00:05:1")
811
            ),
812
            Link(
813
                id_to_interface_mock("00:00:00:00:00:00:00:05:2"),
814
                id_to_interface_mock("00:00:00:00:00:00:00:06:1")
815
            ),
816
            Link(
817
                id_to_interface_mock("00:00:00:00:00:00:00:06:2"),
818
                id_to_interface_mock("00:00:00:00:00:00:00:04:3")
819
            ),
820
            Link(
821
                id_to_interface_mock("00:00:00:00:00:00:00:04:2"),
822
                id_to_interface_mock("00:00:00:00:00:00:00:07:2")
823
            ),
824
        ]
825
826 1
        mock_response.json.return_value = {"paths": paths2["paths"]}
827 1
        mock_requests_post.return_value = mock_response
828 1
        paths = list(DynamicPathManager.get_disjoint_paths(evc, current_path))
829 1
        args = mock_shared.call_args[0]
830 1
        assert args[0] == paths2["paths"][-1]
831 1
        assert args[1] == path_interfaces
832 1
        assert args[2] == path_switches
833 1
        assert len(paths) == 1
834 1
        assert (
835
            [link.id for link in paths[0]] ==
836
            [link.id for link in expected_disjoint_path]
837
        )
838
839 1
    def test_get_shared_components(self):
840
        """Test get_shared_components"""
841 1
        mock_path = {"hops": [
842
            '00:00:00:00:00:00:00:01:1',
843
            '00:00:00:00:00:00:00:01',
844
            '00:00:00:00:00:00:00:01:4',
845
            '00:00:00:00:00:00:00:05:2',
846
            '00:00:00:00:00:00:00:05',
847
            '00:00:00:00:00:00:00:05:3',
848
            '00:00:00:00:00:00:00:02:4',
849
            '00:00:00:00:00:00:00:02',
850
            '00:00:00:00:00:00:00:02:3',
851
            '00:00:00:00:00:00:00:03:2',
852
            '00:00:00:00:00:00:00:03',
853
            '00:00:00:00:00:00:00:03:1'
854
        ]}
855 1
        mock_links = [
856
            ("00:00:00:00:00:00:00:01:2", "00:00:00:00:00:00:00:02:2"),
857
            ("00:00:00:00:00:00:00:02:3", "00:00:00:00:00:00:00:03:2")
858
        ]
859 1
        mock_switches = {"00:00:00:00:00:00:00:02"}
860 1
        actual_lk, actual_sw = DynamicPathManager.get_shared_components(
861
            mock_path, mock_links, mock_switches
862
        )
863 1
        assert actual_lk == 1
864
        assert actual_sw == 1
865